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
1 # class A:
2 # __x=1 # 把数据属性隐藏 _A__x
3 #
4 # def __init__(self,name):
5 # self.__name=name # _A__name
6 #
7 # def __foo(self): # 把函数属性隐藏 def _A__foo(self)
8 # print('run foo')
9 #
10 # def bar(self):
11 # self.__foo() #self._A_foo() 定义阶段已经变了
12 # print('from bar')
13
14
15 # print(A.__dict__)
16 # {'__module__': '__main__', '_A__x': 1, '__init__': <function A.__init__ at 0x0000017AC92B0B70>, '_A__foo': <function A.__foo at 0x0000017AC92B0BF8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
17 # 类定义阶段 __开头发生了变形 __x --> _A__x
18
19 # print(A.__x)
20 # print(A._A__x)
21 # print(A.__foo)
22 # print(A._A__foo)
23 # a=A('alice')
24 # print(a.__dict__)
25 # {'_A__name': 'alice'}
26 # print(a.__name)
27 # print(a._A__name)
28 # a.bar()
29
30 # ---------------------------------------------
31 # class Foo:
32 # def __func(self): #_Foo__func
33 # print('from foo')
34 #
35 # class Bar(Foo):
36 # def __func(self): #_Bar__func
37 # print('from bar')
38
39 # b=Bar()
40 # b.func() # 子类把父类 重名的给覆盖掉了
41
42 # ---------------------------------------------
43 # class B:
44 # __x=1
45 #
46 # def __init__(self,name):
47 # self.__name=name
48
49
50 # # print(B._B__x) # 一般不要这么做!py不让你这么干了
51 # B.__y=2 # 类定义完之后不会发生变形
52 # # print(B.__dict__)
53 # # {'__module__': '__main__', '_B__x': 1, '__init__': <function B.__init__ at 0x00000180405A0B70>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, '__y': 2}
54 #
55 # b=B('alice')
56 # print(b.__dict__) # {'_B__name': 'alice'}
57 #
58 # b.__age=18
59 # print(b.__dict__) #{'_B__name': 'alice', '__age': 18}
60
61 # ---------------------------------------------
62 # class A:
63 # def foo(self):
64 # print('A.foo')
65 #
66 # def bar(self):
67 # print('A.bar')
68 # self.foo() #b.foo
69 #
70 # class B(A):
71 # def foo(self):
72 # print('B.foo')
73 #
74 # b=B()
75 # b.bar()
76
77 # ---------------------------------------------
78 class A:
79 def __foo(self): #_A__foo
80 print('A.foo')
81
82 def bar(self):
83 print('A.bar')
84 self.__foo() #self._A__foo() # 只调自己类的方法 定义时就已经确定好的!
85
86 class B(A):
87 def __foo(self): #_B__foo
88 print('B.foo')
89
90 b=B()
91 b.bar()
2.封装的意义
封装数据属性目的: (封装不是单纯意义上的隐藏)
明确的区分内外,控制外部对隐藏的属性的操作行为
封装方法属性目的:
隔离复杂度 # a=ATM() a.withdraw()
1 # class People:
2 # def __init__(self,name,age):
3 # self.__name=name
4 # self.__age=age
5 #
6 # def tell_info(self): # 接口 设定规则
7 # print('Name:<%s> Age:<%s>'%(self.__name,self.__age))
8 #
9 # def set_info(self,name,age): # 接口 间接的修改 设定规则
10 # if not isinstance(name,str):
11 # print('名字必须是字符串类型')
12 # return
13 # if not isinstance(age,int):
14 # print('年龄必须是数字类型')
15 # return
16 # self.__name=name
17 # self.__age=age
18 #
19 # p=People('alice','18')
20 # p.tell_info()
21
22 # p.set_info('alex',38)
23 # p.tell_info()
24
25 # p.set_info('alex','38')
26 # p.tell_info()
27
28 # -------------------------------------------------------
29 class ATM:
30 def __card(self): # 复杂的流程 给隐藏起来了 外部没必要关心
31 print('插卡')
32 def __auth(self):
33 print('用户认证')
34 def __input(self):
35 print('输入取款金额')
36 def __print_bill(self):
37 print('打印账单')
38 def __take_money(self):
39 print('取款')
40
41 def withdraw(self): # 只有这个是用户 关心的
42 self.__card()
43 self.__auth()
44 self.__input()
45 self.__print_bill()
46 self.__take_money()
47
48 a=ATM()
49 a.withdraw()
3.封装与扩展性
面向对象:可扩展性高
面向对象三大特性:继承 多态 封装
封装的扩展性:
def tell_area(self): # 对使用者来说 不用改变 方式 开发者在类里面修改
1 class Room:
2 def __init__(self,name,owner,weight,length,height):
3 self.name=name
4 self.owner=owner
5
6 self.__weight=weight
7 self.__length=length
8 self.__height=height
9
10 def tell_area(self): # 对使用者来说 不用改变 方式
11 return self.__weight * self.__length * self.__height
12
13
14 r=Room('卫生间','alex',10,10,10)
15 print(r.tell_area())
4.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
-------------------------------
property :
@property
def bmi(self): 必须有个返回值
print(p.bmi) 可以使 函数属性 伪装成 数据属性 bmi 是名词
p.bmi=23 # 不能赋值 can't set attribute bmi 实质是个方法
总结:通过计算得来的方法 可以通过@property 伪装成数据属性
@property 查看 必须有返回值
@name.setter 修改
@name.deleter 删除
1 # class People:
2 # def __init__(self,name,weight,height):
3 # self.name=name
4 # self.weight=weight
5 # self.height=height
6 #
7 # @property
8 # def bmi(self): # 必须要有个返回值
9 # return self.weight / (self.height ** 2)
10 #
11 # p=People('egon',75,1.81)
12 # p.bmi=p.weight / (p.height ** 2)
13 # print(p.bmi)
14
15 # print(p.bmi()) # 函数 是 去做什么! 是动词 对于使用者 产生误解 调了一个动词
16
17 # print(p.bmi) # 使用者可以像 访问数据属性 那样 访问 函数属性
18 # p.height=1.82
19 # print(p.bmi)
20 # p.bmi=23 # 不能赋值 can't set attribute bmi 实质是个方法
21
22 # ------------------------------------------------------------
23 class People:
24 def __init__(self,name):
25 self.__name=name
26
27 @property # 查看
28 def name(self):
29 # print('getter')
30 return self.__name
31
32 @name.setter # 修改 # 前提是 一定被装饰过 property
33 def name(self,val):
34 # print('setter',val)
35 if not isinstance(val,str):
36 print('名字必须是字符串')
37 return
38 self.__name=val
39
40 @name.deleter # 删除
41 def name(self):
42 # print('deleter')
43 print('不允许删除')
44
45
46 p=People('alice')
47 # print(p.name)
48
49 # p.name='alex'
50 # print(p.name)
51
52 del p.name