封装:
一、什么是封装?
装是把一堆属性存起来,封就是把这些属性给隐藏起来。
强调:封装单从字面意思去看等同于隐藏的概念,但其实封装绝对不是单纯意义的隐藏。
封装的终极奥义是明确地区分内外,对外是隐藏的,对内是开放的。
二、为什么要用封装
1、封装数据属性的目的:把数据属性封装起来,然后需要开辟接口给类外部的使用者使用,目的是可以通过接口之上添加控制逻辑,从而严格控制访问者对属性的操作。
class People:
def __init__(self,name,age):
self.__name=name
self.__age=age
def tell_info(self):
# u=input('user>>: ').strip()
# p=input('pwd>>: ').strip()
# if u == 'monicx' and p == '123':
print(self.__name,self.__age)
def set_info(self,name,age):
if type(name) is not str:
raise TypeError('用户名必须为str类型')
if type(age) is not int:
raise TypeError('年龄必须为int类型')
self.__name=name
self.__age=age
p=People('monicx',18)
p.set_info('xxx',19)
p.tell_info()
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()
三、如何封装
封装是把属性隐藏起来,就在属性前面加上__开头(注意不要加__结尾)
注意:
1、其实这种隐藏只是一种语法上的变形,对外不对内
为一个属性名加__开头(注意不要加__结尾),会在类定义阶段将属性名统一变形:_自己的类名__属性名
class Foo:
__x=1111 #_Foo__x=1111
def __init__(self,y):
self.__y=y #self._Foo__y=y
def __f1(self): #_Foo__f1
print('Foo.f1')
def get_y(self):
print(self.__y) # print(self._Foo__y)
obj=Foo(22222)
# print(obj.x)#AttributeError:'Foo' object has no attribute 'x'
# print(obj.__x)#AttributeError: 'Foo' object has no attribute '__x'
# obj.__f1()#AttributeError: 'Foo' object has no attribute '__f1'
# print(obj.y)#AttributeError: 'Foo' object has no attribute 'y'
# print(obj.__y)#AttributeError: 'Foo' object has no attribute '__y'
print(Foo.__dict__)
'''
{'_Foo__x': 1111,,
'_Foo__f1': <function Foo.__f1 at 0x00000000026D9950>,
'get_y': <function Foo.get_y at 0x00000000026D99D8>,}
'''
print(obj._Foo__x)#1111
print(obj._Foo__y)#22222
obj._Foo__f1()#22222
obj.get_y()#22222
2、这种语法意义上变形,只在类定义阶段发生一次,类定义之后,新增的__开头的属性都没有变形的效果
Foo.__aaa=1
print(Foo.__dict__)# '__aaa': 1
obj.__bbb=2
print(obj.__dict__)#{'_Foo__y': 22222, '__bbb': 2}
3、如果父类不想让子类覆盖自己的话,可以在方法名前加__开头
class Foo:
def __f1(self): #_Foo__f1
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.__f1() #obj._Foo__f1()
class Bar(Foo):
def __f1(self): #_Bar__f1
print("Bar.f1")
obj=Bar()
obj.f2()
'''
Foo.f2
Foo.f1
'''
property
property是一种特殊的属性,访问这时会执行一段功能(函数)然后返回这个功能运行结果的值。
例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
'''
class Pepple:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
@property
def bmi(self):
return self.weight/(self.height*self.height)
monicx=Pepple('monicx',63,1.78)
zzzz=Pepple('ZZZZ',55,1.58)
#首先需要明确,bmi是算出的,不是一个固定的值,我们必须编写一个功能,每次调用都会立即计算一个值
# monicx.bmi=monicx.weight/(monicx.height*monicx.height)
# print(monicx.bmi)
# print(zzzz.bmi())
# print(monicx.bmi())
#bmi不是一个功能虽一个特征,使用函数不太好,最好是一个变量属性为好。
#于是我们需要为bmi这个函数添加装饰器(property),将其伪装成一个数据属性
print(zzzz.bmi)
#此时调用zzzz.bim本质就触发函数bmi执行,从而拿到其返回值。
print(monicx.bmi)
下面了解一下poerty其它用处。
class People:
def __init__(self,name):
self.__name=name
@property
def name(self): #obj.name
print('访问用户名。。。')
return self.__name
#修改用户名
@name.setter #obj.name='monicx'
def name(self,x):
if type(x) is not str:
raise TypeError('名字必须是str类型')
self.__name=x
#删除用户名
@name.deleter
def name(self):
print('可以在这里加上判断用户的管理权限。是否让其删除')
del self.__name
obj=People('egon')
print(obj.name)
del obj.name
# obj.name