7.11 封装
封装不是单纯意义上的隐藏
首先封装怎么实现隐藏
属性的隐藏(数据属性,函数属性)
python类中,以__开头命名的属性会被隐藏。
注意:__开头__结尾的是python内置函数的意思,不是隐藏。
实际上,隐藏就是属性名的变形操作,在类定义的时候发生变形。
- 隐藏属性的特点:
- 在类的外部无法用定义时的属性名来调用:obj__AttrName
在类的内部可以直接用定义时的属性名来调用: obj__AttrName
在类定义的时候隐藏属性都发生了变化,所以也直接用变形后的属性名调用了
子类无法覆盖父类__开头的属性
class A:
__x = 1 # 实际上隐藏是在定义类的时候变形了属性名,变成了 _A__x
def __init__(self, name):
self.__name = name # 变形成 _A__name
def __foo(self): # 变形成 _A__foo
print('run foo')
def bar(self):
self.__foo() # 类调用的时候也被变形成 _A__foo了,所以跟上面变化后相同,所以在类内部可以调用
print('from bar')
# print(A.__x) # 会报错
# print(A.__foo) # 会报错
a = A('a')
# print(a.__name) # 会报错
print(a._A__name) # 隐藏属性的特点1,不过不推荐这么访问
# 我们看看他们的名称空间发生了什么
print(A.__dict__)
print(a.__dict__)
a.bar() # 隐藏属性的特点2
执行结果:
a
{'__module__': '__main__', '_A__x': 1, '__init__': <function A.__init__ at 0x0000000001E5A6A8>,
'_A__foo': <function A.__foo at 0x0000000001E5A7B8>, '__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'_A__name': 'a'}
run foo
from bar
我们可以看到,在定义类的时候,隐藏的属性发生了变形。
隐藏属性特点第3点代码例:
class Foo:
def __func(self): # _Foo__func
print('from foo')
class Bar(Foo):
def __func(self): # _Bar__func
print('from bar')
b = Bar()
print(Bar.__dict__)
总结这种变形需要注意的问题:
- 这种异常不是真正意义上的隐藏,只是变形。
这种变形只在类定义的时候就会发生,且只发生一次。类定义之后就全都变形了。
在定义之后给类增加一个__开头的属性,定义的属性不会变形,而是以原名添加到类中。
```
class B:
__x = 1def __init__(self, name): self.__name = name
b = B('b')
b.__age = 11
print(b.__dict__)
B.__y = 2
print(B.dict) # __y 会原样的增加到类的命名空间中
print(b.dict) # __age 会原样的增加到类的命名空间中
执行结果:
{'_B__name': 'b'}
{'module': 'main', '_B__x': 1, 'init': <function B.__init__ at 0x000000000288A6A8>, 'dict': <attribute 'dict' of 'B' objects>, 'weakref': <attribute 'weakref' of 'B' objects>, 'doc': None, '__y': 2}
{'_B__name': 'b', '__age': 11}
```在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为隐藏的
```
class A:
def __foo(self):
print('A.foo')def bar(self): print('A.bar') self.__foo()
class B(A):
def __foo(self):
print('B.foo')b = B()
b.bar() # 如果foo不是隐藏属性,调用父类的bar里的foo会调用B类中的,而使用了隐藏属性就会调用A类的__foo
执行结果:
A.bar
A.foo
```
封装的意义
1. 封装数据属性的意义:来明确区分内外,控制外部对隐藏属性的操作。
在类的外部不能直接访问封装属性,但是在类的内部还是可以直接访问。
从外部只能间接访问封装的属性。我们可以通过定义接口函数来控制访问的方法。
在外部不能直接修改类中的属性,只能通过接口改,
例:
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('年龄必须是int类型')
return
self.__name = name
self.__age = age
p = People('a',11)
p.tell_info()
p.set_info('b', '12')
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()
a = ATM()
a.withdraw()
封装与可扩展性
我们有一个Room的类,开始没有height属性,后来追加。 因为用户调用的是接口方法,所以我们内部不论怎么修改,用户都是以相同的方式调用。 这就体现出了一种可扩展性
class Room:
def __init__(self, name, owner, weight, lenth, height): # 后增加了height
self.name = name
self.owner = owner
self.__weight = weight
self.__lenth = lenth
self.__height = height # 追加height属性
def tell_result(self): #
return self.__weight * self.__lenth * self.__height # 追加了height
r = Room('卫生间','a', 10, 10, 10) # 追加属性
# 用户调用接口是完全没有变化的
r.tell_result()