Python 面向对象之封装和装饰器property
2024-01-07 22:14:15
Python 面向对象之封装和装饰器property
【一】概念
- 封装是面向对象的三大特征之一
- 封装:将属性和方法打包在一起,并对外部提供接口,控制外部对内部数据的访问和修改
- 封装有助于隐藏对象的内部细节,提供更清晰的结构,提高了代码的安全性和可维护性
【二】隐藏属性和方法
【1】概念
- 类的设计者不想使用者直接访问到属性,就可以将属性进行隐藏,有隐藏属性和隐藏方法
python
的class
机制采用双下划线开头的方式进行隐藏属性和方法(私有属性、私有方法)- 但是并没有真正意义上的隐藏,隐藏的机制:
- 在类的定义阶段,双下划线开头的属性和方法都会发生变形
- 属性变形:_类名__属性
- 方法变形:_类名__方法
- 在类的外部可以通过访问变形,可以使用这个私有属性或者方法
- 然而类的内部是可以通过双下划线访问的,这是因为在检查类体代码语法时统一发生了变形(类定义阶段)
- 这种变形指在类的定义阶段(检查类体语法时)发生一次,之后再定义的双下划线开头的属性和方法都不会变形,即可以直接通过双下划线开头访问
【2】代码解释
# 类的设计者不想使用者直接访问到属性,就可以将属性进行隐藏,有隐藏属性和隐藏方法
# python的class机制采用双下划线开头的方式进行隐藏属性和方法(私有属性、私有方法)
class A:
# 类属性
name = "bruce"
# 私有属性(隐藏属性)
__private_age = 18
# 实例方法
def instance_method(self):
print("这是是实例方法")
# 私有方法(隐藏方法)
def __private_method(self):
print("这是私有方法")
a = A()
# 访问属性
print(a.name) # bruce
# print(a.__private_age) # 报错,访问不到
print(A.name) # bruce
# print(A.__private_age) # 报错,访问不到
# 访问方法
a.instance_method() # 这是是实例方法
# a.__private_method() # 报错,访问不到
A.instance_method(a) # 这是是实例方法
# A.__private_method() # # 报错,访问不到
# 隐藏的机制:
# 在类的定义阶段,双下划线开头的属性和方法都会发生变形
# 属性变形:_类名__属性
# 方法变形:_类名__方法
# 在类的外部可以通过访问变形,可以使用这个私有属性或者方法
# 然而类的内部是可以通过双下划线访问的,这是因为在检查类体代码语法时统一发生了变形(类定义阶段)
# 这种变形指在类的定义阶段(检查类体语法时)发生一次,之后再定义的双下划线开头的属性和方法都不会变形,即可以直接通过双下划线开头访问
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) # bruce
# 通过变形访问
print(a._A__private_age) # 18
# 访问方法
a.instance_method()
# 这是是实例方法
# name:bruce age:18
# 通过变形访问
a._A__private_method()
# 这是是实例方法
# name:bruce age:18
# 类外部添加私有属性(并不是私有属性)
a.__private_age = 20
print(a.__private_age) # 20
print(a._A__private_age) # 18
【三】开放接口
- 定义了属性和方法,那么这个属性和方法就一定是有一定的作用的,不能仅仅只是隐藏起来
- 即隐藏不是目的,目的是为了更安全更好的使用
【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.money -= 100 # 无法访问私有属性
bruce_atm.withdraw_money(100) # 取款成功:你还有0元
# 充值
# bruce_atm.money += 100 # 无法访问私有
bruce_atm.recharge_money(100) # 充值成功:你还有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
# 余:1
【2】应用场景二:装饰器链
- 私有属性在类里面定义好了,我们希望在类的外部获取它的值、修改他的值和删除这个值,可以使用提供的接口方法,但是我们更想让他像一个普通的属性一样,直接通过
.
号运算符有进行取值、赋值和删除值 - 注意:三个方法的名字必须一样,形参可以不通过
- 注意:三个方法的名字必须一样,形参可以不通过
- 注意:三个方法的名字必须一样,形参可以不通过
class A:
__x = "aaa"
# 在取值的前面只需要使用装饰器property
@property
def x(self):
print("取值方法")
return self.__x
# 在赋值方法的前面需要加上取值方法的函数名.setter
# 这个方法名需要和取值的方法名相同
@x.setter
def x(self, value):
print("赋值方法")
self.__x = value
# 在赋值方法的前面需要加上取值方法的函数名.deleter
# 这个方法名需要和取值的方法名相同
@x.deleter
def x(self):
print("删除值方法")
del self.__x
a = A()
print(a.x) # 取值方法
# aaa
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
# 使用property函数创建这个属性
# 不用self.传入,直接传入地址
# 位置传参(取值方法地址、赋值方法地址、删除值方法地址)
x = property(get_x, set_x, del_x)
a = A()
print(a.x) # 取值方法
# aaa
a.x = "bb" # 赋值方法
del a.x # 删除值方法
【五】总结
文章来源:https://blog.csdn.net/weixin_48183870/article/details/135394975
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!