Python——封装
封装介绍
- 封装是面向对象三大特性最核心得一个特性
- 封装<—>整合
一、隐藏属性
将封装得属性进行隐藏操作
# 如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果(只是一个变形)
class Foo:
__x=1
def __f1(self):
print('from test')
# 这种变形需要注意的问题:
# 1、在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,
# 然后就可以访问了,如Foo._Foo__x,所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种
# 语法意义上的变形。
class Foo:
__x = 1 # _Foo__x
def __f1(self): # _Foo__f1
print('from test')
print(Foo.__dict__) # '_Foo__x': 1 '_Foo__f1': <function Foo.__f1 at 0x00000234854CB160>
print(Foo._Foo__x)
print(Foo._Foo__f1)
# 2、这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形
class Foo:
__x = 1 # _Foo__x = 1
def __f1(self): # _Foo__f1
print('from test')
def f2(self):
print(self.__x) # print(self._Foo__x)
print(self.__f1) # print(self._Foo__f1)
print(Foo.__x)
print(Foo.__f1)
obj=Foo()
obj.f2()
# 3、这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形
class Foo:
__x = 1 # _Foo__x = 1
def __f1(self): # _Foo__f1
print('from test')
def f2(self):
print(self.__x) # print(self._Foo__x)
print(self.__f1) # print(self._Foo__f1)
Foo.__y=3
print(Foo.__dict__) # '__y': 3
print(Foo.__y) # 3
二、开放接口
- 定义属性就是为了使用的,所以隐藏不是目的而是为了特殊使用
2.1 隐藏数据接口
-
将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
>>> class Teacher: ... def __init__(self,name,age): #将名字和年纪都隐藏起来 ... self.__name=name ... self.__age=age ... def tell_info(self): #对外提供访问老师信息的接口 ... print('姓名:%s,年龄:%s' %(self.__name,self.__age)) ... def set_info(self,name,age): #对外提供设置老师信息的接口,并附加类型检查的逻辑 ... if not isinstance(name,str): ... raise TypeError('姓名必须是字符串类型') ... if not isinstance(age,int): ... raise TypeError('年龄必须是整型') ... self.__name=name ... self.__age=age ... >>> >>> t=Teacher('lili',18) >>> t.set_info(‘LiLi','19') # 年龄不为整型,抛出异常 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 11, in set_info TypeError: 年龄必须是整型 >>> t.set_info('LiLi',19) # 名字为字符串类型,年龄为整形,可以正常设置 >>> t.tell_info() # 查看老师的信息 姓名:LiLi,年龄:19
2.2 隐藏函数接口
-
隔离复杂度,便于使用者调用函数
>>> class ATM: ... def __card(self): #插卡 ... print('插卡') ... def __auth(self): #身份认证 ... print('用户认证') ... def __input(self): #输入金额 ... print('输入取款金额') ... def __print_bill(self): #打印小票 ... print('打印账单') ... def __take_money(self): #取钱 ... print('取款') ... def withdraw(self): #取款功能 ... self.__card() ... self.__auth() ... self.__input() ... self.__print_bill() ... self.__take_money() ... >>> obj=ATM() >>> obj.withdraw()
三、property
-
装饰器是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加新功能的可调用对象
-
property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性
# 案例1 class People: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height # 定义函数的原因1: # 1、从bmi的公式上看,bmi应该是触发功能计算得到的 # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值 # 说白了,每次都是需要临时计算得到的 # 但是bmi听起来更像是一个数据属性,而非功能 @property def bmi(self): return self.weight / (self.height ** 2) obj1 = People('umi', 62.5, 1.8) # print(obj1.bmi()) print(obj1.bmi) obj1.height=1.86 # print(obj1.bmi()) print(obj1.bmi) # 案例2 class People: def __init__(self, name): self.__name = name def get_name(self): return self.__name def set_name(self, val): if type(val) is not str: print('必须传入str类型') return self.__name = val def del_name(self): print('不让删除') # del self.__name name=property(get_name, set_name, del_name) obj1=People('egon') # print(obj1.get_name()) # obj1.set_name('EGON') # print(obj1.get_name()) # obj1.del_name() # 人正常的思维逻辑 print(obj1.name) obj1.name=18 del obj1.name # 案例3(推荐) class People: def __init__(self, name): self.__name = name @property def name(self): # obj1.name return self.__name @name.setter def name(self, val): # obj1.name='EGON' if type(val) is not str: print('必须传入str类型') return self.__name = val @name.deleter def name(self): # del obj1.name print('不让删除') # del self.__name obj1=People('egon') # 人正常的思维逻辑 print(obj1.name) obj1.name= 'EGON' print(obj1.name) del obj1.name