Python魔法方法之描述符

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中非常强大的一种机制,通过它我们可以轻易地实现对类的属性访问、操作、控制等操作。描述符可以用于控制类型安全、域过滤、数学运算等场景,让我们编写更稳健、更健壮的代码。

后端开发标签