Python 面向对象之封装和装饰器property

Python 面向对象之封装和装饰器property

【一】概念

  • 封装是面向对象的三大特征之一
  • 封装:将属性方法包在一起,并对外部提供接口,控制外部对内部数据的访问和修改
  • 封装有助于隐藏对象的内部细节,提供更清晰的结构,提高了代码的安全性可维护性

【二】隐藏属性和方法

【1】概念

  • 类的设计者不想使用者直接访问到属性,就可以将属性进行隐藏,有隐藏属性隐藏方法
  • pythonclass机制采用双下划线开头的方式进行隐藏属性和方法私有属性、私有方法
  • 但是并没有真正意义上的隐藏,隐藏的机制
    • 在类的定义阶段,双下划线开头的属性和方法都会发生变形
    • 属性变形:_类名__属性
    • 方法变形:_类名__方法
    • 类的外部可以通过访问变形可以使用这个私有属性或者方法
    • 然而类的内部是可以通过双下划线访问的,这是因为在检查类体代码语法时统一发生变形类定义阶段
    • 这种变形指在类的定义阶段检查类体语法时发生一次,之后再定义的双下划线开头的属性和方法都不会变形,即可以直接通过双下划线开头访问

【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         # 删除值方法

【五】总结

Python 面向对象之封装和装饰器property_第1张图片

你可能感兴趣的:(python,开发语言)