Python 面向对象之封装和装饰器property
【一】概念
- 封装是面向对象的三大特征之一
- 封装:将属性和方法打包在一起,并对外部提供接口,控制外部对内部数据的访问和修改
- 封装有助于隐藏对象的内部细节,提供更清晰的结构,提高了代码的安全性和可维护性
【二】隐藏属性和方法
【1】概念
- 类的设计者不想使用者直接访问到属性,就可以将属性进行隐藏,有隐藏属性和隐藏方法
python
的class
机制采用双下划线开头的方式进行隐藏属性和方法(私有属性、私有方法)- 但是并没有真正意义上的隐藏,隐藏的机制:
- 在类的定义阶段,双下划线开头的属性和方法都会发生变形
- 属性变形:_类名__属性
- 方法变形:_类名__方法
- 在类的外部可以通过访问变形,可以使用这个私有属性或者方法
- 然而类的内部是可以通过双下划线访问的,这是因为在检查类体代码语法时统一发生了变形(类定义阶段)
- 这种变形指在类的定义阶段(检查类体语法时)发生一次,之后再定义的双下划线开头的属性和方法都不会变形,即可以直接通过双下划线开头访问
【2】代码解释
class A:
name = "bruce"
__private_age = 18
def instance_method(self):
print("这是是实例方法")
def __private_method(self):
print("这是私有方法")
a = A()
print(a.name)
print(A.name)
a.instance_method()
A.instance_method(a)
class A:
name = "bruce"
__private_age = 18
def instance_method(self):
print("这是是实例方法")
print(f"name:{self.name} age:{self.__private_age}")
self.__private_method()
def __private_method(self):
print("这是私有方法")
a = A()
print(a.name)
print(a._A__private_age)
a.instance_method()
a._A__private_method()
a.__private_age = 20
print(a.__private_age)
print(a._A__private_age)
【三】开放接口
- 定义了属性和方法,那么这个属性和方法就一定是有一定的作用的,不能仅仅只是隐藏起来
- 即隐藏不是目的,目的是为了更安全更好的使用
【1】隐藏属性
【1】概念
- 隐藏属性:将属性隐藏起来,限制类外部对数据的直接访问,只用通过类提供的接口来允许类外部间接访问和操做,接口之上我们可以添加额外的逻辑来对数据进行处理,这样更安全可靠
【2】代码解释
- 在银行系统中,其中金额是极其重要的数据,所以我们要隐藏这个属性,提供接口给类外部间接操作数据
class AtmUser:
def __init__(self, name):
self.username = name
self.__money = 100
def withdraw_money(self, money: int):
if type(money) != int:
print("取款失败:输入非法")
elif money > self.__money:
print("取款失败:你没有这么多钱")
else:
self.__money -= money
print(f"取款成功:你还有{self.__money}元")
def recharge_money(self, money: int):
if type(money) != int:
print("充值失败:输入非法")
else:
self.__money += money
print(f"充值成功:你还有{self.__money}元")
bruce_atm = AtmUser("bruce")
bruce_atm.withdraw_money(100)
bruce_atm.recharge_money(100)
【2】隐藏方法
【1】概念
【2】代码解释
- 同样的在银行系统中,客户的操作有存钱取钱等,银行却需要更多的方法,比如检验你登录或者插卡没有,用户的身份验证、金额的验证等方法,这些方法不需要给客户提供,所以要隐藏这些方法,隔离复杂度
class AtmUser:
def __init__(self, name):
self.username = name
self.__money = 100
def __card_check(self):
print("检查是否插卡")
pass
def __check_user_info(self):
print("检查用户信息")
pass
def __money_chcek(self):
print("检查金额是否满足要求")
pass
def withdraw_money(self, money: int):
self.__card_check()
self.__check_user_info()
self.__money_chcek()
self.__money -= 100
【四】装饰器property
property
是一种特殊的装饰器,用于将类的方法伪装成类的属性- 它能够将一个方法伪装成只读属性,使得在访问这个属性时可以像访问普通属性一样,实际上还是调用相应的方法
【1】应用场景一:MyDivmod
- 在内置函数
divmod
中输入被除数和除数就可以得到商和余数 - 商和余数可以用方法计算得到,但是这两个更像是属性,所以可以用装饰器property装饰成属性
class MyDivmod:
def __init__(self, dividend, divisor):
self.__dividend = dividend
self.__divisor = divisor
@property
def discuss(self):
return self.__dividend // self.__divisor
@property
def remainder(self):
return self.__dividend % self.__divisor
result = MyDivmod(9,4)
print(f"商:{result.discuss}")
print(f"余:{result.remainder}")
【2】应用场景二:装饰器链
- 私有属性在类里面定义好了,我们希望在类的外部获取它的值、修改他的值和删除这个值,可以使用提供的接口方法,但是我们更想让他像一个普通的属性一样,直接通过
.
号运算符有进行取值、赋值和删除值 - 注意:三个方法的名字必须一样,形参可以不通过
- 注意:三个方法的名字必须一样,形参可以不通过
- 注意:三个方法的名字必须一样,形参可以不通过
class A:
__x = "aaa"
@property
def x(self):
print("取值方法")
return self.__x
@x.setter
def x(self, value):
print("赋值方法")
self.__x = value
@x.deleter
def x(self):
print("删除值方法")
del self.__x
a = A()
print(a.x)
a.x = "bb"
del a.x
【3】应用场景三:经典的属性定义方式
- 在场景二中每个取值赋值删除值的方法前面都需要添加装饰器property对应的内容
- 为了简化代码,我们可以使用property()函数来创建一个属性,并将相应的取值、赋值和删除值的方法传递给它
- 注意:三个方法的名字不能相同,形参可以不同,传入property函数默认是位置传参
- 注意:三个方法的名字不能相同,形参可以不同,传入property函数默认是位置传参
- 注意:三个方法的名字不能相同,形参可以不同,传入property函数默认是位置传参
class A:
__x = "aaa"
def get_x(self):
print("取值方法")
return self.__x
def set_x(self, value):
print("赋值方法")
self.__x = value
def del_x(self):
print("删除值方法")
del self.__x
x = property(get_x, set_x, del_x)
a = A()
print(a.x)
a.x = "bb"
del a.x
【五】总结