封装:

一、什么是封装?

装是把一堆属性存起来,封就是把这些属性给隐藏起来。

 强调:封装单从字面意思去看等同于隐藏的概念,但其实封装绝对不是单纯意义的隐藏。

            封装的终极奥义是明确地区分内外,对外是隐藏的,对内是开放的。

二、为什么要用封装

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