封装

封装

封装有两个层面:封装数据、封装功能。
在python中没有常量一样,只是将变量名用大写的方式来提醒你,但仍然可以修改。封装数据就是将数据隐藏起来,通过在名称前加双下划线:__attr 但是并没有真正的隐藏,只是做了一次变形,让你无法通过原来的名字访问到:__attr >>> _ClassName__attr, 通过后者,你仍然可以访问到。
有两点要注意:
1. 这种变形操作只发生在类的定义阶段或对象的定义阶段(实例化)。
2. 在类的外部,无法直接使用变形的属性,而在类的内部可以。

栗子一:

class Teacher:
    __school = 'oldboy' # >>> _Teacher__school
    def __init__(self,name,salary):
        self.name=name
        self.__salary=salary    # >>> _Teacher__salary

    def tell(self):
        print('salary is: ',self.__salary)  
        # print('salary is: ',self._Teacher__salary)

t = Teacher('egon', 3000)
t.__salary  # 类外部访问,提示没有__salary属性
t.tell() # 通过类内部的tell方法可以访问__salary(其实在定义阶段就已经变形过了)

栗子二:

class A():
    def foo(self):
        print('from A foo ')
        self.bar()

    def bar(self):
        print('from A bar')

class B(A):
    def bar(self):
        print('from B bar')

obj = B()
obj.foo()
'''
# obj找foo属性: obj > B > A 找到了,执行 print('from A foo ')
from A foo 
# 继续往下执行,self.bar(),找bar属性,obj > B,找到了,执行 print('from B bar')
from B bar
'''

现在,我们把上面这个栗子中的bar属性隐藏起来:

class A():
    def foo(self):
        print('from A foo ')
        self.__bar()    # >>> _A__bar  :变形发生在定义阶段

    def __bar(self):    # >>> _A__bar
        print('from A bar')

class B(A):
    def __bar(self):    # >>> _B__bar
        print('from B bar')

obj = B()
obj.foo()
''' 输出结果变了:
from A foo 
from A bar
'''

接口

单纯的封装没有意义,必须提供接口。
python并不会检查输入类型,通过封装和接口,在接口中人为进行限制:

class People:
    def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def tell_info(self):
        print('''
        ---- info ----
        NAME:   %s 
        AGE:    %s
        '''%(self.__name,self.__age))

    def set(self,val1,val2):    # python本身不会检查输入类型,可以通过在接口中人为的限制输入类型。
        if not isinstance(val1, str):
            raise TypeError('name must be str')
        if not isinstance(val2, int):
            raise TypeError('age must be int')
        self.__name = val1
        self.__age = val2

egon = People('egon', 18)
egon.set('林海峰',20)
egon.tell_info()

property

property是一个内置函数,访问它时会执行一段功能(函数)然后返回值。
作用:把被装饰的函数伪装成数据属性。

class People():
    def __init__(self,name,height,weight):
        self.__name=name
        self.__height=height
        self.__weight=weight

    def bmi(self):  # 计算体质指数bmi
        res = self.__weight / (self.__height ** 2)
        print('''
        name:   %s
        bmi:    %s
        '''%(self.__name,round(res,2)))

egon = People('egon',18,1.75,65)
egon.bmi()  # bmi更应该像是一个人的属性,而不是执行了一个方法
'''
    name:   egon
    bmi:    21.22

'''

上面这个栗子,对于使用者来说,很容易察觉到是加括号执行了一段函数,这时property函数就派上用场了:

class People():
    def __init__(self,name,height,weight):
        self.__name=name
        self.__height=height
        self.__weight=weight

    def bmi(self):  # 计算体质指数bmi
        res = self.__weight / (self.__height ** 2)
        return res  # 比起打印结果,返回一个值,更像在访问一个数据
    bmi = property(bmi) # 调用property函数把bmi函数的执行结果赋值给bmi

egon = People('egon',1.75,65)
egon.bmi    
# 拿到的是一个值。这样对于使用者来说,就像是访问数据属性一样简单自然,
# 而不必知道其实是执行了一个函数后的计算结果,这就将背后的逻辑隐藏起来。
# 而且不论函数内的逻辑如何改变,使用者都不用关心,只要通过bmi这个接口,就可以拿到想要的结果。

装饰器的本质是任何可以调用的对象,因此我们可以用装饰器的语法进一步简化:

class People():
    def __init__(self,name,height,weight):
        self.__name=name
        self.__height=height
        self.__weight=weight

    @property
    def bmi(self):  
        res = self.__weight / (self.__height ** 2)
        return res  


egon = People('egon',1.75,65)
egon.bmi

setter, deleter方法

class Teacher:
    def __init__(self,name,permission=False):
        self.__name = name
        self.permission = permission    # 标志位,下面的函数检测用

    @property
    def name(self): # obj通过obj.name这个接口访问self.__name
        return self.__name

    @name.setter    # @propery创建的另一个装饰器
    def name(self,val):
        if not isinstance(val,str):
            raise TypeError('%s must be str'% val)
        self.__name = val # 通过类型检查后,可以为self.__name赋值

    @name.deleter
    def name(self):
        if not self.permission: # 初始化时默认标志位为False
            raise PermissionError('不让删')
        del self.__name
        # self.permission = False   也可以在操作完成后,恢复标志位

obj = Teacher('egon')
obj.name = 'EGON'  #  修改对象的self.__name值,>>> @name.setter ,修改成功
print(obj.name)     #  访问对象的self.__name >>> @property
del obj.name # 删除对象的self.__name属性,>>> @name.deleter 标志位判断失败,不让删。

上面的这个栗子体现了property提供统一访问接口的功能,使用者似乎是在访问或操作一个对象的属性,但其实是通过三个函数实现的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值