封装
封装不是单纯意义上的隐藏。
一、封装中如何隐藏属性:
属性前加 “__” 就行了,这种方式只在类定义阶段发生, 如: __x = 1
class A:
__x = 1 # 隐藏属性 __x
def __foo(self): # 隐藏属性 __foo
print("from foo")
def bar(self):
self.__foo() # 在类内部可以直接调用内部隐藏属性
print("from bar")
a = A()
# print(a.__x) # 在类外部直接调用要出错
# a.__foo() # 在类外部直接调用会出错
print(a._A__x) # 正确输出:1
a._A__foo() # 正确输出:from foo
a.bar() # 正确输出:from foo 和 from bar
print(A.__dict__) # 通过__dict__查看到__x已经变形为 _A__x , __foo变形为了 _A__foo
这种变形的特点:(在类定义阶段就变形)
1. 在类外部无法直接obj.__AttrName
2. 在类内部可以直接是使用obj.__AttrName
3. 子类无法覆盖父类 __ 开头的属性
针对第3点来验证一下:
class Foo:
def __func(self): # 变成了_Foo__func
print("from foo")
class Bar(Foo): # 继承了父类
def __func(self): # 变成了_Bar__func
print("from bar")
验证只能在定义阶段变形:
class B:
def __init__(self, name):
self.__name = name
b = B("alex")
B.__x = 1 # 没有变形,说明这种方式只在类定义阶段发生,这里已经是定义之后了
print(B.__dict__) # 可以看出没有变
b.__age = 18
print(b.__dict__) # __age依然没有变形,输出:'_B__name': 'alex', '__age': 18}
在来看个例子:
class D:
def foo(self):
print("from D.foo")
def bar(self):
print("from D.bar")
self.foo() # 这里实际上调的E.foo
class E(D):
def foo(self):
print("E.foo")
e = E()
e.bar() # 输出:from D.bar 和 E.foo
这个例子里面 bar里调的实际是E的foo,因为调用请求是由E的对象发起的,那如果我就要在bar中就是调用D的foo怎么办呢?
给方法前加上__ ,由于在定义阶段加上__就会变形,所以D类的foo就变成了_D__foo了,E类里的foo就变成了_E__foo了
class D:
def __foo(self):
print("from D.foo")
def bar(self):
print("from D.bar")
self.__foo() # 加上__后就调用时就变成了 e._D__foo了,而e对象没有,去E找,E没有就去父类D找,在D找到了,所以输出 from D.foo
class E(D):
def __foo(self):
print("E.foo")
e = E()
e.bar() # 输出:from D.bar 和 from D.foo
二、封装的意义
封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为
# 封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为
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("alex", 18)
p.tell_info() # 输出:Name:<alex> Age:<18>
p.set_info("Alex", 38)
p.tell_info() # 输出:Name:<Alex> Age:<38>
封装方法:隔离复杂度
对于使用者而言无需关心内部如何实现,只需要调一个简单的接口就行了。
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()
输出:
插卡
用户认证
输入取款金额
打印账单
取款
三、封装与可扩展性
class Room:
def __init__(self, name, owner, weight, length):
self.name = name
self.owner = owner
self.__weight = weight
self.__lenght = length
def tell_area(self):
return self.__weight * self.__lenght
r = Room("客厅", "alex", 10, 10)
print(r.tell_area())
输出:100
class Room:
def __init__(self, name, owner, weight, length, height):
self.name = name
self.owner = owner
self.__weight = weight
self.__lenght = length
self.__height = height
def tell_area(self):
return self.__weight * self.__lenght * self.__height
r = Room("客厅", "alex", 10, 10, 10)
print(r.tell_area())
输出:1000
四、property
property是一个装饰器,使得方法可以像访问属性一样去访问。
BMI指数例子:
# 常规做法
class People:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height
def bmi(self):
return self.weight / (self.height ** 2)
p = People("alex", 50, 1.60)
print(p.bmi()) # 输出:19.531249999999996
# property改进
class People:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height
@property
def bmi(self):
return self.weight / (self.height ** 2)
p = People("alex", 50, 1.60)
print(p.bmi) # 输出:19.531249999999996
p.height = 1.75
print(p.bmi) # 输出:16.3265306122449
# p.bmi = 2223333 # 报错,不能赋值,因为bmi其实并不是属性,而是一个方法,只不过是通过property将方法做的像属性了
另一个例子:
# 传统做法
class People:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
p = People("alex")
print(p.get_name())
# 加property使之可以用p.name 访问
class People:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
p = People("alex")
print(p.name)
# 修改、删除属性
class People:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter # 必须要首先有 name属性被property装饰
def name(self, name):
if not isinstance(name, str):
print("名字必须是字符串类型")
return
self.__name = name
@name.deleter # 必须要首先有 name属性被property装饰
def name(self):
print("不允许删除")
p = People("alex")
print(p.name) # 输出:alex
# 修改
p.name = "ALEX" # 给p.name 赋值
print(p.name) # 输出:ALEX ,修改成功
# 删除
del p.name # 输出:不允许删除
共有 0 条评论