1.封装之如何实现属性的隐藏
封装: __x=1 # 把数据属性隐藏 (如何实现隐藏) 类定义阶段 __开头发生了变形 __x --> _A__x
特点: 1.在类外部无法直接:obj.__AttrName 2.在类内部是可以直接使用:obj.__AttrName # 为什么会这样?python 如何实现的 !类定义阶段已经变形 #__x --> _A__x #self._A_foo() 3.子类无法覆盖父类__开头的属性 它两根本不是一个名字 #_Foo__func #_Bar__func总结: 这种变形需要注意的问题: 1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N eg:print(A._A__x) 2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形 eg: b.__age=18 {'_B__name': 'alice', '__age': 18} 3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 eg: def __foo(self): #_A__foo# class A:# __x=1 # 把数据属性隐藏 _A__x## def __init__(self,name):# self.__name=name # _A__name## def __foo(self): # 把函数属性隐藏 def _A__foo(self)# print('run foo')## def bar(self):# self.__foo() #self._A_foo() 定义阶段已经变了# print('from bar')# print(A.__dict__)# { '__module__': '__main__', '_A__x': 1, '__init__':, '_A__foo': , '__dict__': , '__weakref__': , '__doc__': None}# 类定义阶段 __开头发生了变形 __x --> _A__x# print(A.__x)# print(A._A__x)# print(A.__foo)# print(A._A__foo)# a=A('alice')# print(a.__dict__)# { '_A__name': 'alice'}# print(a.__name)# print(a._A__name)# a.bar()# ---------------------------------------------# class Foo:# def __func(self): #_Foo__func# print('from foo')## class Bar(Foo):# def __func(self): #_Bar__func# print('from bar')# b=Bar()# b.func() # 子类把父类 重名的给覆盖掉了# ---------------------------------------------# class B:# __x=1## def __init__(self,name):# self.__name=name# # print(B._B__x) # 一般不要这么做!py不让你这么干了# B.__y=2 # 类定义完之后不会发生变形# # print(B.__dict__)# # { '__module__': '__main__', '_B__x': 1, '__init__': , '__dict__': , '__weakref__': , '__doc__': None, '__y': 2}## b=B('alice')# print(b.__dict__) # { '_B__name': 'alice'}## b.__age=18# print(b.__dict__) #{ '_B__name': 'alice', '__age': 18}# ---------------------------------------------# class A:# def foo(self):# print('A.foo')## def bar(self):# print('A.bar')# self.foo() #b.foo## class B(A):# def foo(self):# print('B.foo')## b=B()# b.bar()# ---------------------------------------------class A: def __foo(self): #_A__foo print('A.foo') def bar(self): print('A.bar') self.__foo() #self._A__foo() # 只调自己类的方法 定义时就已经确定好的!class B(A): def __foo(self): #_B__foo print('B.foo')b=B()b.bar()
2.封装的意义
封装数据属性目的: (封装不是单纯意义上的隐藏) 明确的区分内外,控制外部对隐藏的属性的操作行为 封装方法属性目的: 隔离复杂度 # a=ATM() a.withdraw(
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:
# class People:# def __init__(self,name,age):# self.__name=name# self.__age=age## def tell_info(self): # 接口 设定规则# print('Name:<%s> Age:<%s>'%(self.__name,self.__age))## def set_info(self,name,age): # 接口 间接的修改 设定规则# if not isinstance(name,str):# print('名字必须是字符串类型')# return# if not isinstance(age,int):# print('年龄必须是数字类型')# return# self.__name=name# self.__age=age## p=People('alice','18')# p.tell_info()# p.set_info('alex',38)# p.tell_info()# p.set_info('alex','38')# p.tell_info()# -------------------------------------------------------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()a=ATM()a.withdraw()
3.封装与扩展性
面向对象:可扩展性高面向对象三大特性:继承 多态 封装封装的扩展性: def tell_area(self): # 对使用者来说 不用改变 方式 开发者在类里面修改
class Room: def __init__(self,name,owner,weight,length,height): self.name=name self.owner=owner self.__weight=weight self.__length=length self.__height=height def tell_area(self): # 对使用者来说 不用改变 方式 return self.__weight * self.__length * self.__heightr=Room('卫生间','alex',10,10,10)print(r.tell_area())
4.property的使用
Python内置的@property
装饰器就是负责把一个方法变成属性调用的:
@property
的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
注意到这个神奇的@property
,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。
还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:
小结
@property
广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
property : @property def bmi(self): 必须有个返回值 print(p.bmi) 可以使 函数属性 伪装成 数据属性 bmi 是名词 p.bmi=23 # 不能赋值 can't set attribute bmi 实质是个方法 总结:通过计算得来的方法 可以通过@property 伪装成数据属性 @property 查看 必须有返回值 @name.setter 修改 @name.deleter 删除
练习
请利用@property
给一个Screen
对象加上width
和height
属性,以及一个只读属性resolution
:
class Screen(object): @property def resolution(self): return self.width * self.height# 测试:s = Screen()s.width = 1024s.height = 768print('resolution =', s.resolution)if s.resolution == 786432: print('测试通过!')else: print('测试失败!')#输出resolution = 786432测试通过!