一文搞懂Python对象的描述器机制

Written by

in

文章目录
  • 在Python中,“描述器”是一种能够自定义对象属性访问(获取、设置、删除)行为的协议。它是实现@property、类属性、ORM字段、类型检查的底层支撑。 一句话解释:只要一个对象定义了__get__、__set__或__delete__等特殊方法,并作为另一个类的类属性,这个对象就成了描述器! 默认对属性的访问控制是从对象的字典里面 (__dict__) 中获取 (get) , 设置 (set) 和删除 (delete) 。 举例来说, a.x 的查找顺序是, a.__dict__['x'] , 然后 type(a).__dict__['x'] , 然后找 type(a) 的父类 ( 不包括元类 (metaclass) ).如果查找到的值是一个描述器, Python 就会调用描述器的方法来重写默认的控制行为。
  • 方法 作用 用法场景 __get__ 获取属性时调用 动态计算、懒加载 __set__ 设置属性时调用 数据验证、只读限制 __delete__ 删除属性时调用 管控属性生命周期 最简单的描述器雏形: class DemoDescriptor: def __get__(self, instance, owner): print(‘执行了__get__’) return 42 class MyClass: x = DemoDescriptor() obj = MyClass() print(obj.x) # 输出42,中间会打印“执行了__get__”
  • 你用过的@property,其实就是“非数据描述器”的语法糖: class Demo: @property def val(self): return 666 底层实现和你定义带__get__的对象如出一辙。
  • 目录
    • 一、什么是Python描述器?
    • 二、描述器协议:三大魔法方法
    • 三、入门实战:搞懂描述器工作的方式
      • 3.1 数据描述器 VS 非数据描述器
      • 3.2 数据描述器基础案例
    • 四、@property 与描述器的关系
      • 五、进阶:自定义描述器封装与通用实现
        • 5.1 描述器工厂 基于装饰器快速生成校验字段
        • 5.2 只读属性描述器

      在Python中,“描述器”是一种能够自定义对象属性访问(获取、设置、删除)行为的协议。它是实现@property、类属性、ORM字段、类型检查的底层支撑。

      一句话解释:只要一个对象定义了__get____set____delete__等特殊方法,并作为另一个类的类属性,这个对象就成了描述器!

      默认对属性的访问控制是从对象的字典里面 (__dict__) 中获取 (get) , 设置 (set) 和删除 (delete) 。

      举例来说, a.x 的查找顺序是, a.__dict__['x'] , 然后 type(a).__dict__['x'] , 然后找 type(a) 的父类 ( 不包括元类 (metaclass) ).如果查找到的值是一个描述器, Python 就会调用描述器的方法来重写默认的控制行为。

      方法 作用 用法场景
      __get__ 获取属性时调用 动态计算、懒加载
      __set__ 设置属性时调用 数据验证、只读限制
      __delete__ 删除属性时调用 管控属性生命周期

      最简单的描述器雏形:

      class DemoDescriptor:
          def __get__(self, instance, owner):
              print('执行了__get__')
              return 42
      
      class MyClass:
          x = DemoDescriptor()
      
      obj = MyClass()
      print(obj.x)  # 输出42,中间会打印“执行了__get__”
      

      • 数据描述器 : 同时定义了__get____set__
      • 非数据描述器 : 只定义了__get__

      数据描述器拥有更高优先级,会屏蔽实例字典上的同名属性!

      class Typed:
          def __init__(self, name, typ):
              self.name = name
              self.typ = typ
      
          def __get__(self, instance, owner):
              return instance.__dict__[self.name]
      
          def __set__(self, instance, value):
              if not isinstance(value, self.typ):
                  raise TypeError(f'属性{self.name}必须是{self.typ}')
              instance.__dict__[self.name] = value
      
      class Person:
          age = Typed('age', int)    # 用描述器控制
          name = Typed('name', str)
      
      p = Person()
      p.age = 18            # 自动类型检查
      p.name = '小明'
      # p.age = 'not int'   # 抛出TypeError
      

      你用过的@property,其实就是“非数据描述器”的语法糖:

      class Demo:
          @property
          def val(self):
              return 666
      

      底层实现和你定义带__get__的对象如出一辙。

      def typed_field(name, typ):
          class _TypedDescriptor:
              def __get__(self, instance, owner):
                  return instance.__dict__[name]
              def __set__(self, instance, value):
                  if not isinstance(value, typ):
                      raise TypeError(f"属性{name}必须是{typ}")
                  instance.__dict__[name] = value
          return _TypedDescriptor()
      
      class Student:
          age = typed_field('age', int)
          name = typed_field('name', str)
      
      stu = Student()
      stu.age = 18    # √
      # stu.age = 'oops' # TypeError
      

      class ReadOnly:
          def __init__(self, value):
              self._value = value
          def __get__(self, instance, owner):
              return self._value
      
      class System:
          version = ReadOnly('1.0.0')
      
      print(System.version)   # '1.0.0'
      

      到此这篇关于一文搞懂Python对象的描述器机制的文章就介绍到这了,更多相关Python对象描述器内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

      您可能感兴趣的文章:

      • python的描述器descriptor详解
      • Python虚拟机中描述器的王炸应用分享
      • python虚拟机之描述器实现原理与源码分析
      • 深入理解Python虚拟机中描述器的实现原理
      • 深入了解Python中描述器的使用

      站内搜索