• Python基础-面向对象编程之特性(property)


    Python面向对象编程之特性(property)

    一、统一访问原则

    通常,我们访问实例或类的属性时,将返回所存储的相关值。而特性(property)是一种特殊的属性,访问它时会计算它的值。

    请看下面的例子:

    import math
    
    class Circle():
      def __init__(self, radius):
        self.radius = radius  # 保存半径
    
      # Circle的一些附加特性
      
      @property
      def area(self):
        return math.pi * self.radius**2
    
      @property
      def perimeter(self):
        return 2 * math.pi * self.radius
    
    if __name__ == '__main__':
      c = Circle(4.0)
      print(f'圆的半径为:{c.radius}')
      print(f'圆的面积为:{c.area}')
      print(f'圆的周长为:{c.perimeter}')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    运行效果如下:

    20221126005942

    从这个例子中,我们可以看到,Circle实例存储了一个变量c.radius。而c.area和c.perimeter是根据该值计算得来的。@property装饰器支持以简单属性的形式访问后面的方法,无需像平时一样添加额外的()来调用该方法。对象的使用者很难发现正在计算一个属性,除非在试图重新定义该属性时生成了错误消息。

    这种特性使用方式遵循所谓的统一访问原则。实际上,如果定义一个类,尽可能保持编程接口的统一总是不错的。如果没有特性(property),将会以简单属性(如c.radius)的形式访问对象的某些属性,而其他属性将以方法(如c.area())的形式进行访问。费力去了解何时添加额外的()会带来不必要的混淆。而特性可以解决此问题。

    二、使用特性进行拦截操作

    特性(property)还可以拦截操作,以设置和删除属性。这是通过向特性附加其他的setter和deleter方法来实现的,比如下面的代码:

    class Foo():
      def __init__(self, name):
        self.__name = name
    
      @property
      def name(self):
        return self.__name
    
      @name.setter
      def name(self, value):
        if not isinstance(value, str):
          raise TypeError('must be a string')
        self.__name = value
    
      @name.deleter
      def name(self):
        raise TypeError('cannot delete name')
    
    f = Foo('lilei')
    n = f.name            # 调用f.name() get函数
    f.name = 'hanmeimei'  # 调用setter name(f, 'hanmeimei')
    f.name = 14           # 调用setter name(f, 45)  -> TypeError
    del f.name            # 调用deleter name(f)     -> TypeError
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这个例子中,实现使用@property装饰器和相关方法将属性name定义为只读特性。后面的@name.setter和@name.deleter装饰器将其他方法与name属性上的设置和删除操作相关联。这些方法的名称必须与原始特性的名称完全匹配。在这些方法中,请注意实际的名称值是存储在属性self.__name中。所存储属性的名称无需遵循任何约定,但它必须与特性名称不同,以便将它与特性的名称区分开来。

    三、老式写法

    以前的老代码中可能还会看到以下这种写法:

    class Foo():
      def getname(self):
        return self.__name
    
      def setname(self, value):
        self.__name = value
    
      def delname(self):
        raise TypeError('cannot delete name')
    
      name = property(getname, setname, delname)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这种老的写法仍然可用,但新的装饰器版本会让类看起来更完美一些。例如,如果使用装饰器,get、set、delete这些函数将不会显示为方法。

  • 相关阅读:
    计算机毕业设计(附源码)python足球爱好者服务平台
    Debian安装Docker环境
    C++系列之list的模拟实现
    华为被迫开源,从认知到落地SpringBoot企业级实战手册(完整版)
    服务的追踪-Sleuth
    互联网商业模式有哪些?
    基于FPGA的FIR数字滤波器设计(quartus和vivado程序都有)。
    iOS实现代码混淆
    这是一个高度不确定时代
    WPS如何转PDF格式?WPS转PDF怎么转?
  • 原文地址:https://blog.csdn.net/hubing_hust/article/details/128047208