Python - 描述符



Python 描述符

Python 描述符 是一种自定义对象属性的访问、赋值和删除的方式。它们提供了一种强大的机制,通过定义获取、设置和删除其值的方法来管理属性的行为。描述符通常用于实现属性、方法和属性验证。

描述符 是任何实现了至少一个方法(例如 __get__、__set__ 和 __delete__)的对象。这些方法控制如何访问和修改属性的值。

Python 描述符如何工作?

当在实例上访问属性时,Python 会在实例的类中查找该属性。如果找到该属性并且它是一个描述符,则 Python 会调用相应的描述符方法,而不是简单地返回属性的值。这允许描述符控制在属性访问期间发生的事情。

描述符协议是一种低级机制,Python 中许多高级功能都使用它,例如属性、方法、静态方法和类方法。描述符可用于实现延迟加载、类型检查和计算属性等模式。

描述符方法

Python 描述符 包括三个主要方法,即 __get__()、__set__() 和 __delete__()。正如我们上面已经讨论过的,这些方法分别控制属性访问、赋值和删除的行为。

1. __get__() 方法

描述符中的 __get__() 方法 是 Python 中描述符协议的关键部分。它被调用以从实例或类中检索属性的值。了解 __get__() 方法 的工作原理对于创建可以以复杂方式管理属性访问的自定义描述符至关重要。

语法

以下是 Python 描述符 __get__ 方法 的语法:

def __get__(self, instance, owner):
   """
   instance: the instance that the attribute is accessed through, or None when accessed through the owner class.
   owner: the owner class where the descriptor is defined.
   """

参数

以下是此方法的参数:

  • self: 描述符实例。
  • instance: 访问属性的类的实例。当通过类而不是实例访问属性时,它为 None。
  • owner: 拥有描述符的类。

示例

以下是 __get__() 方法的基本示例,其中在访问 obj.attr 时返回存储的值 _value

class Descriptor:
   def __get__(self, instance, owner):
      if instance is None:return self
      return instance._value

class MyClass:
   attr = Descriptor()

   def __init__(self, value):
      self._value = value

obj = MyClass(42)
print(obj.attr)  

输出

42

2. __set__() 方法

__set__() 方法 是 Python 中描述符协议的一部分,用于控制设置属性值的行为。当由描述符管理的属性被赋予新值时,__set__() 方法 被调用,允许用户自定义或强制执行赋值规则。

语法

以下是 Python 描述符 __set__() 方法 的语法:

def __set__(self, instance, value):
    """
    instance: the instance of the class where the attribute is being set.
    value: the value to assign to the attribute.
    """

参数

以下是此方法的参数:

  • self: 描述符实例。
  • instance: 设置属性的类的实例。
  • value: 赋给属性的值。

示例

以下是 __set__() 方法的基本示例,其中确保赋给 attr 的值是整数:

class Descriptor:
    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise TypeError("Value must be an integer")
        instance._value = value

class MyClass:
    attr = Descriptor()

    def __init__(self, value):
        self.attr = value

obj = MyClass(42)
print(obj.attr)  
obj.attr = 100
print(obj.attr)  

输出

<__main__.Descriptor object at 0x000001E5423ED3D0>
<__main__.Descriptor object at 0x000001E5423ED3D0>

3. __delete__() 方法

描述符协议中的 __delete__() 方法 允许我们控制从实例中删除属性时发生的事情。当删除属性时,这对于管理资源、清理或强制执行约束很有用。

语法

以下是 Python 描述符 __delete__() 方法 的语法:

def __delete__(self, instance):
    """
    instance: the instance of the class from which the attribute is being deleted.
    """

参数

以下是此方法的参数:

  • self: 描述符实例。
  • instance: 删除属性的类的实例。

示例

以下是 __set__() 方法的基本示例,其中确保赋给 attr 的值是整数:

class LoggedDescriptor:
   def __init__(self, name):
      self.name = name

   def __get__(self, instance, owner):
      return instance.__dict__.get(self.name)

   def __set__(self, instance, value):
      instance.__dict__[self.name] = value

   def __delete__(self, instance):
      if self.name in instance.__dict__:
         print(f"Deleting {self.name} from {instance}")
         del instance.__dict__[self.name]
      else:
         raise AttributeError(f"{self.name} not found")

class Person:
   name = LoggedDescriptor("name")
   age = LoggedDescriptor("age")

   def __init__(self, name, age):
      self.name = name
      self.age = age

# Example usage
p = Person("Tutorialspoint", 30)
print(p.name)  
print(p.age)   

del p.name    
print(p.name) 

del p.age    
print(p.age)   

输出

Tutorialspoint
30
Deleting name from <__main__.Person object at 0x0000021A1A67E2D0>
None
Deleting age from <__main__.Person object at 0x0000021A1A67E2D0>
None

Python 描述符的类型

在 Python 中,描述符 可以根据它们实现的方法大致分为两种类型。它们是:

  • 数据描述符
  • 非数据描述符

让我们详细了解这两种 Python 描述符,以便我们更好地理解。

1. 数据描述符

数据描述符 是一种 Python 描述符,它定义了 __get__()__set__() 方法。这些描述符优先于实例属性,这意味着即使存在同名的实例属性,也会始终调用描述符的 __get__()__set__() 方法。

示例

下面是一个数据描述符的示例,它确保属性始终为整数,并记录访问和修改操作。

class Integer:
   def __get__(self, instance, owner):
      print("Getting value")
      return instance._value

   def __set__(self, instance, value):
      print("Setting value")
      if not isinstance(value, int):
         raise TypeError("Value must be an integer")
      instance._value = value

   def __delete__(self, instance):
      print("Deleting value")
      del instance._value

class MyClass:
   attr = Integer()

# Usage
obj = MyClass()
obj.attr = 42  
print(obj.attr)  
obj.attr = 100  
print(obj.attr)  
del obj.attr   

输出

Setting value
Getting value
42
Setting value
Getting value
100
Deleting value

2. 非数据描述符

非数据描述符是 Python 中的一种描述符类型,它仅定义了__get__()方法。与数据描述符不同,非数据描述符可以被实例属性覆盖。这意味着如果存在同名的实例属性,则它将优先于非数据描述符。

示例

以下是非数据描述符的一个示例,如果实例上未设置属性,则它提供一个默认值。

class Default:
   def __init__(self, default):
      self.default = default

   def __get__(self, instance, owner):
      return getattr(instance, '_value', self.default)

class MyClass:
   attr = Default("default_value")

# Usage
obj = MyClass()
print(obj.attr)  
obj._value = "Tutorialspoint"
print(obj.attr) 

输出

default_value
Tutorialspoint

数据描述符与非数据描述符

了解 Python 描述符中数据描述符非数据描述符之间的区别对于有效地利用它们的功能至关重要。

标准 数据描述符 非数据描述符
定义 实现 __get__()、__set__() 方法,以及可选的 __delete__() 方法。 仅实现 __get__() 方法。
方法 __get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)(可选)
__get__(self, instance, owner)
优先级 优先于实例属性。 被实例属性覆盖。
用例 属性验证和强制执行,
托管属性(例如,属性),
记录属性访问和修改,
强制执行只读属性。
方法绑定,
缓存和,
提供默认值。

最后,我们可以说 Python 中的描述符提供了一种强大的机制来管理属性的访问和修改。了解数据描述符和非数据描述符之间的区别以及它们的适当用例对于创建健壮且可维护的 Python 代码至关重要。

通过利用描述符协议,开发人员可以实现高级行为,例如类型检查、缓存和只读属性。

广告