1. 引言
Python中的描述符是一种强大的机制,在OOP编程中经常被使用。描述符是一个对象,给其他对象提供了一些额外的操作控制能力,例如属性的查找、修改和删除等。Python中的属性访问是通过一种叫做“属性协议”的协议来完成的。当我们访问一个对象的属性时,Python首先会在对象自身中查找属性,如果找不到则在所属类中查找,如果还是找不到则在父类中依次查找。这种查找的方式被称为“属性访问优先级”。
2. 描述符的基本概念
2.1 描述符的定义
描述符是一种Python对象,该对象实现了以下三种方法中的一种或多种:
__get__(self, obj, type=None): 当访问描述符属性时,该方法被调用,返回属性的值。
__set__(self, obj, value): 当设置描述符属性的值时,该方法被调用。
__delete__(self, obj): 当删除描述符属性时,该方法被调用。
描述符可以是一个经典类或新式类中的方法,也可以是一个实现了上述方法的自定义对象。
2.2 描述符的优先级
描述符的优先级是通过Python的“属性访问优先级”规则来决定的。Python的属性访问优先级规则是:
如果属性是一个描述符,那么调用描述符的方法。
如果属性是个类成员(类属性),那么返回该值。
如果属性是实例成员(实例属性),那么返回该值。
2.3 描述符的应用
描述符常见的应用场景是在属性访问控制上。这种情况下,描述符可以为一个类的属性提供数据验证、类型转换、计算属性等等,并对属性的获取、修改、删除等操作进行控制。
3. 描述符的实例
3.1 验证属性值的描述符
下面的代码实现了一个属性的验证描述符,确保属性值在指定的范围内。
class Value:
def __init__(self, lower, upper):
self.lower = lower
self.upper = upper
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if value < self.lower or value > self.upper:
raise ValueError(f"Value must be between {self.lower} and {self.upper}")
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
class MyClass:
x = Value(0, 10)
y = Value(-5, 5)
在上面的代码中,Value是一个描述符类,它有两个属性lower和upper,分别表示属性值的最小值和最大值。当读取属性时,__get__方法被调用,返回该属性值;当设置属性时,__set__方法被调用,先验证属性值的合法性,然后再设置属性值;__set_name__方法被调用时,保存属性名的字符串。
3.2 计算属性的描述符
下面的代码实现了一个计算属性的描述符,该属性的值是由其他属性计算得来的。
class Area:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
return instance.width * instance.height
class Rectangle:
width = 0
height = 0
area = Area()
在上面的代码中,Area是一个描述符类,它的__get__方法返回矩形面积,由矩形的width和height属性决定。
3.3 类级别的属性描述符
下面的代码实现了一个类级别的属性描述符。
class NonNegative:
def __init__(self, default):
self.default = default
def __get__(self, instance, owner):
return owner.__dict__.get(self.name, self.default)
def __set__(self, instance, value):
if value < 0:
raise ValueError(f"{self.name} must be non-negative")
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
class MyClass:
x = NonNegative(0)
y = NonNegative(0)
在上面的代码中,NonNegative是一个类级别的描述符,它确保该类的属性值永远为非负数。默认值由构造函数的参数指定。
4. 结论
描述符是Python中非常强大的一种机制,通过它我们可以轻易地实现对类的属性访问、操作、控制等操作。描述符可以用于控制类型安全、域过滤、数学运算等场景,让我们编写更稳健、更健壮的代码。