Python 简明教程
Python - Descriptors
Python Descriptors
Python Descriptors 是一种定制对象属性的访问、赋值和删除的方式。它们通过定义获取、设置和删除属性值的方法,提供一个管理属性行为的强大机制。描述符经常用于实现属性、方法和属性验证。
Python Descriptors are a way to customize the access, assignment and deletion of object attributes. They provide a powerful mechanism for managing the behavior of attributes by defining methods that get, set and delete their values. Descriptors are often used to implement properties, methods and attribute validation.
descriptor 是至少实现 get 、 set 和 delete 中的一种方法的任何对象。这些方法用于控制属性值的访问和修改方式。
A descriptor is any object that implements at least one of the methods such as get, set and delete. These methods control how an attribute’s value is accessed and modified.
How Python Descriptors Work?
当访问某个实例上的某个属性时,Python 会查找该实例的 class 中的该属性。如果找到了该属性,并且它是一个描述符,那么 Python 将调用相应的描述符方法代替简单地返回该属性的值。这将允许描述符控制属性访问期间所发生的事情。
When an attribute is accessed on an instance then Python looks up the attribute in the instance’s class. If the attribute is found and it is a descriptor then Python invokes the appropriate descriptor method instead of simply returning the attribute’s value. This allows the descriptor to control what happens during attribute access.
描述符协议是一种低级机制,在 Python 的许多高级功能中,例如属性、方法、静态方法和类方法中使用。描述符可以用来实现懒加载、类型检查和计算属性等模式。
The descriptor protocol is a low-level mechanism that is used by many high-level features in Python such as properties, methods, static methods and class methods. Descriptors can be used to implement patterns like lazy loading, type checking and computed properties.
Descriptor Methods
Python Descriptors 包含三个主要方法,即 get ()、 set () 和 delete 。正如我们之前所讨论的,这些方法分别控制属性访问、赋值和删除的行为。
Python Descriptors involve three main methods namely get(), set() and delete(). As we already discussed above these methods control the behavior of attribute access, assignment and deletion, respectively.
1. The get() Method
描述符中的 get() method 是 Python 中描述符协议的关键部分。它被调用用于从实例或类中检索属性值。理解 get() method 的工作方式对于创建可以以复杂的方式管理属性访问的自定义描述符至关重要。
The get() method in descriptors is a key part of the descriptor protocol in Python. It is called to retrieve the value of an attribute from an instance or from the class. Understanding how the get() method works is crucial for creating custom descriptors that can manage attribute access in sophisticated ways.
以下为 Python Descriptor get method 的语法:
The following is the syntax of Python Descriptor get method −
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.
"""
下面是该方法的参数:
Below are the parameters of this method −
-
self: The descriptor instance.
-
instance: The instance of the class where the attribute is accessed. It is None when the attribute is accessed through the class rather than an instance.
-
owner: The class that owns the descriptor.
以下为 get () 方法的简单示例,其中它在访问 obj.attr 时返回存储的值 _value :
Following is the basic example of get() method in which it returns the stored value _value when obj.attr is accessed −
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)
Output
42
2. The set() Method
set() method 是 Python 中描述符协议的一部分,它用于控制设置属性值的行为。当由描述符管理的属性被赋值新值时, set() method 将被调用,从而允许用户定制或强制执行赋值规则。
The set() method is part of the descriptor protocol in Python and is used to control the behavior of setting an attribute’s value. When an attribute managed by a descriptor is assigned a new value then the set() method is called by allowing the user to customize or enforce rules for the assignment.
以下是 Python Descriptor set() method 的语法:
The following is the syntax of Python Descriptor set() method −
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.
"""
下面是该方法的参数:
Below are the parameters of this method −
-
self: The descriptor instance.
-
instance: The instance of the class where the attribute is being set.
-
value: The value being assigned to the attribute.
以下是 set () 方法的基本示例,它确保分配给 attr 的值是一个整数 -
Following is the basic example of set() method in which ensures that the value assigned to attr is an integer −
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)
Output
<__main__.Descriptor object at 0x000001E5423ED3D0>
<__main__.Descriptor object at 0x000001E5423ED3D0>
3. The delete() Method
描述符协议中的 delete() method 允许我们控制当属性从实例中删除时发生的事情。这对于在删除属性时管理资源、清理或执行约束很有用。
The delete() method in the descriptor protocol allows us to control what happens when an attribute is deleted from an instance. This can be useful for managing resources, cleaning up or enforcing constraints when an attribute is removed.
以下是 Python Descriptor delete() method 的语法 -
The following is the syntax of Python Descriptor delete() method −
def __delete__(self, instance):
"""
instance: the instance of the class from which the attribute is being deleted.
"""
下面是该方法的参数:
Below are the parameters of this method −
-
self: The descriptor instance.
-
instance: The instance of the class where the attribute is being deleted.
以下是 set () 方法的基本示例,它确保分配给 attr 的值是一个整数 -
Following is the basic example of set() method in which ensures that the value assigned to attr is an integer −
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)
Output
Tutorialspoint
30
Deleting name from <__main__.Person object at 0x0000021A1A67E2D0>
None
Deleting age from <__main__.Person object at 0x0000021A1A67E2D0>
None
Types of Python Descriptors
在 Python 中, descriptors 可以根据它们实现的方法大致分为两种类型。它们是 -
In Python descriptors can be broadly categorized into two types based on the methods they implement. They are −
-
Data Descriptors
-
Non-data Descriptors
让我们详细了解两种类型的 python 描述符,以便更好地理解。
Let’s see about the two types of python descriptors in detail for our better understanding.
1. Data Descriptors
Data descriptors 是 Python 中的一种描述符类型,它定义了 get()*and *set() 方法。这些描述符优先于实例属性,这意味着描述符的 get()*and *set() 方法总是被调用,即使存在具有相同名称的实例属性。
Data descriptors are a type of descriptor in Python that define both get()*and *set() methods. These descriptors have precedence over instance attributes which meand that the descriptor’s get()*and *set() methods are always called, even if an instance attribute with the same name exists.
下面是一个数据描述符的示例,它确保属性始终是整数,并记录访问和修改操作 -
Below is the example of a data descriptor that ensures an attribute is always an integer and logs access and modification operations −
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
Output
Setting value
Getting value
42
Setting value
Getting value
100
Deleting value
2. Non-data Descriptors
Non-data descriptors 是 Python 中的一种描述符类型,它只定义 get() 方法。与数据描述符不同,非数据描述符可以被实例属性覆盖。这意味着如果存在具有相同名称的实例属性,则它将优先于非数据描述符。
Non-data descriptors are a type of descriptor in Python that define only the get() method. Unlike data descriptors, non-data descriptors can be overridden by instance attributes. This means that if an instance attribute with the same name exists then it will take precedence over the non-data descriptor.
以下是一个非数据描述符的示例,如果属性未在实例中设置,则它提供一个默认值 -
Following is an example of a non-data descriptor that provides a default value if the attribute is not set on the instance −
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)
Output
default_value
Tutorialspoint
Data Descriptors Vs. Non-data Descriptors
理解 Data Descriptors 和 Non-data Descriptors 之间的差异对于有效利用它们的这些功能至关重要。
Understanding the differences between Data Descriptors and Non-data Descriptors of python Descriptors is crucial for leveraging their capabilities effectively.
最后我们可以说 Descriptors 在 Python 中提供了一个强大的机制来管理属性访问和修改。理解数据描述符和非数据描述符之间的差异及其适当的用例对于创建鲁棒且可维护的 Python 代码至关重要。
Finally we can say Descriptors in Python provide a powerful mechanism for managing attribute access and modification. Understanding the differences between data descriptors and non-data descriptors as well as their appropriate use cases is essential for creating robust and maintainable Python code.
通过利用描述符协议,开发人员可以实现高级行为,如类型检查、缓存和只读属性。
By leveraging the descriptor protocol developers can implement advanced behaviors such as type checking, caching and read-only properties.