Python面向对象之封装
01
封装简介
封装是面向对象三大特性最核心的一个特性,封装指的是将对象的属性信息隐藏在对象内部,不允许外部直接访问对象内部信息,而是通过该类提供的方法来实现对内部信息的访问和操作,从而确保了数据的安全,在属性名前加_或者__前缀,就会实现一个对外隐藏属性的效果。实际上封装在Python中就是一个约定。
02
进入操作
第一阶段;
首先我们定义学生“张三”的信息;
# 定义学生张三的个人信息name = "张三"age = 18sex = "男"
# 这三个变量可以用来形容张三这个人(当然你也可以使用字典,列表去描述),但是我们用三个变量去形容这个人,有什么弊端?弊端:太零散,如果学生太多,需要有很多变量去描述,为了解决这个问题,因此才有了类。
第二阶段;
定义一个类来保存学生的个人信息;
class Student(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sexone = Student('张三',17,'男') # 这里的one就相当于一个学生张三,所有的属性都封装到了one变量中。这里的Student类,相当于一个模板。只要给定它name,age,sex,它就能创建出一个学生对象。two = Student('aaa',16,'男')three = Student('bbb',15,'女')four = Student('ccc',14,'男')five = Student('ddd',13,'女')print(f'姓名:{one.name} 年龄:{one.age} 性别:{one.sex}')# output:姓名:张三 年龄:17 性别:男
此时是不是已经体验到了类所带来的便利。
第三阶段;
class Student(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sexone = Student('张三',18,'男')one.name = '钢铁侠'one.age = -28one.sex = '女'print(f'姓名:{one.name} 年龄:{one.age} 性别:{one.sex}')# output:姓名:钢铁侠 年龄:-28 性别:女
问题:我们在外部能随意访问到对象one的属性,并且随意修改,这样数据是不安全的,因此我们需要将属性隐藏起来,那我们应该如何去操作呢?
第四阶段;
class Student(object): def __init__(self,name,age,sex): self.__name = name self.__age = age self.__sex = sexone = Student('张三',18,'男')# print(one.__name) # 报错,无该属性# print(one.name) # 报错,无该属性print(f'姓名:{one._Student__name} 年龄:{one._Student__age} 性别:{one._Student__sex}')# output:姓名:张三 年龄:18 性别:男one._Student__age = 20# 对age属性进行修改print(f'姓名:{one._Student__name} 年龄:{one._Student__age} 性别:{one._Student__sex}')# output:姓名:张三 年龄:20 性别:男
问题:此时发现,我们虽然不能使用one.name或者one.__name访问到该属性。但是我们可使用one._Student__age访问到对象的age属性并且能修改。说明python在设置私有属性的时候,只是把属性的名字换成了其他的名字。
总结:类中以_或者__的属性,都是私有属性,禁止外部调用,虽然我们可以通过特殊的手段获取到,并且赋值,但是最好不要这么做(约定俗成)
问题:现在我将name,age,sex设置为私有属性,但是我又想让他们通过我指定的接口去访问或者修改我的属性,那么应该如何实现?
第五阶段;
class Student(object): def __init__(self,name,age,sex): self._name = name self._age = age self._sex = sex def get_name(self): # 此函数用来获取名字 print('调用了get_name方法') return self._name def get_age(self): # 此函数用来获取年龄 # print('调用了get_age方法') return self._age def get_sex(self): # 此函数用来获取性别 # print('调用了get_sex方法') return self._sex def set_name(self,name): # 此函数用来修改名字 if 1 < len(name) < 10: # 对名字长度进行判断 print('调用了set_name方法') self._name = name else: print('请输入合法名字') def set_age(self,age): # 此函数用来修改年龄 if 0 < age < 150: print('调用了set_age方法') self._age = age else: print('请输入合法年龄') def set_sex(self,sex): # 此函数用来修改性别 if sex == '男' or sex == '女': print('调用了set_sex方法') self._sex = sex else: print('请输入合法性别')one = Student('张三',20,'男')print(f'姓名:{one.get_name()} 年龄:{one.get_age()} 性别:{one.get_sex()}')# output: 调用了get_name方法 ;姓名:张三 年龄:20 性别:男one.set_name("李四")print(f'姓名:{one.get_name()} 年龄:{one.get_age()} 性别:{one.get_sex()}')# output: 调用了set_name方法 ; 姓名:李四 年龄:20 性别:男
总结: 这样我们就自定义了自己属性的接口,它的好处在于:规避脏数据。
问题:使用接口设置获取数据 和 使用点方法(one.name = 18 或者print(one.name))设置数据相比, 点方法使用更方便,我们有什么方法达到既能使用点方法,同时又能让点方法直接调用到我们的接口?其实Python已经帮我们实现了,让我们一起看一下吧!
第六阶段;
class Student(object): def __init__(self,name,age,sex): self._name = name self._age = age self._sex = sex @property def name(self): # 此函数用来获取名字 print("调用了get_name方法") return self._name @property def age(self): # 此函数用来获取年龄 # print("调用了get_age方法") return self._age @property def sex(self): # 此函数用来获取性别 # print("调用了get_sex方法") return self._sex @name.setter def name(self,name): # 此函数用来修改名字 if 1 < len(name) < 10: # 对名字长度进行判断 print("调用了set_name方法") self._name = name else: print("请输入合法名字") @age.setter def age(self,age): # 此函数用来修改年龄 if 0 < age < 150: # 对年龄进行判断 print("调用了set_age方法") self._age = age else: print("请输入合法年龄") @sex.setter def sex(self,sex): # 此函数用来修改性别 if sex == "男" or sex == "女": # 对性别进行了判断 print("调用了set_sex方法") self._sex = sex else: print("请输入合法性别")one = Student("张三",20,"男")print(f'姓名:{one.name} 年龄:{one.age} 性别:{one.sex}')# output: 调用了get_name方法 ;姓名:张三 年龄:20 性别:男one._name = "王五"# 调用了get_name方法; 姓名:王五 年龄:20 性别:男one.name = "王五"print(f'姓名:{one.name} 年龄:{one.age} 性别:{one.sex}')# output: 调用了set_name方法; 调用了get_name方法; 姓名:李四 年龄:20 性别:男
总结1:虽然one.name 也可以修改对象属性,但是并没有调用set_name方法,失去了封装的意义,封装这个东西,如果狭隘的理解就可以认为我通过代码对别的开发者做的一个隐晦的提示,告诉你该属性已被封装建议通过内部提供的方法去修改。
总结2:
1、封装是面向对象的三大特性之一。
2、封装指的是隐藏对象中一些不希望被外部所访问到的属性或方法。
3、如何隐藏一个对象中的属性?
- 将对象的属性名,修改为一个外部不知道的名字。
4、如何获取(修改)对象中的属性?
- 需要提供一个getter和setter方法使外部可以访问到属性。
- getter 获取对象中的指定属性(get_属性名)。
- setter 用来设置对象的指定属性(set_属性名)。
5、使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性。
- 隐藏了属性名,使调用者无法随意的修改对象中的属性。
- 增加了getter和setter方法,很好的控制的属性是否是只读的。
- 如果希望属性是只读的,则可以直接去掉setter方法。
- 如果希望属性不能被外部访问,则可以直接去掉getter方法。
- 使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的。
6、使用getter方法获取属性,使用setter方法设置属性。
7、可以在读取属性和修改属性的同时做一些其他的处理。
8、使用getter方法可以表示一些计算的属性。
9、使用 @property 装饰器时,接口名不必与属性名相同。
10、凡是赋值语句,就会触发set方法。凡是获取属性值,就会触发get方法。
END
小编微信就放在下面,你愿加不加~~ 还有记得转发,转发,转发。。。