Python 描述符(Descriptor)入门

  • Post category:Python

Python描述符(Descriptor)入门

简介

Python描述符(Descriptor)是一个高级概念,允许程序员将某些功能捆绑到类属性上。描述符可以实现特定的访问控制、计算属性和方法绑定等功能。

描述符是Python用于实现属性访问控制的重要机制之一。可以将描述符看作是实现对属性赋值所产生的行为进行拦截的机制。

这里将详细讲解Python描述符的实现方法及使用场景。

标准的Python描述符方法

__get__(self, instance, owner)

当一个属性被访问时,__get__方法被调用。其中,self是访问属性对象(对象的实例),instance是访问对象的属性的实例本身,owner是类对象。

以下示例说明:

class Descriptor:
    def __get__(self, instance, owner):
        print(f"get {self} from {instance} - {owner}")
        return self

class Test:
    attr = Descriptor()

t = Test()
t.attr

输出:

get <__main__.Descriptor object at 0x7fda7ac53a90> from <__main__.Test object at 0x7fda7ac53e20> - <class '__main__.Test'>
<__main__.Descriptor object at 0x7fda7ac53a90>

__set__(self, instance, value)

当一个属性被赋值时,__set__方法被调用。其中,self是访问属性对象,instance是访问对象的属性的实例本身,value是被赋的值。

以下示例说明:

class Descriptor:
    def __set__(self, instance, value):
        print(f"set {self} to {value} from {instance}")
        instance._attr = value

class Test:
    attr = Descriptor()

t = Test()
t.attr = 1

输出:

set <__main__.Descriptor object at 0x7fda7ac53400> to 1 from <__main__.Test object at 0x7fda7ac53940>

__delete__(self, instance)

当一个属性被删除时,__delete__方法被调用。其中,self是访问属性对象,instance是访问对象的属性的实例本身。

以下示例说明:

class Descriptor:
    def __delete__(self, instance):
        print(f"delete {self} from {instance}")
        del instance._attr

class Test:
    attr = Descriptor()

t = Test()
t.attr = 1
del t.attr

输出:

set <__main__.Descriptor object at 0x7ff180079820> to 1 from <__main__.Test object at 0x7ff1800796a0>
delete <__main__.Descriptor object at 0x7ff180079820> from <__main__.Test object at 0x7ff1800796a0>

描述符应用场景

限制属性值的取值范围

描述符可以用来限制属性值的取值范围。可以通过定义一个描述符来限制属性值在一定范围内。以下示例限制属性值在0到100之间:

class RangeValue:
    def __init__(self, minimum=None, maximum=None):
        self.minimum = minimum
        self.maximum = maximum
    def __get__(self, instance, owner):
        return instance._attr
    def __set__(self, instance, value):
        if self.minimum is not None and value < self.minimum:
            raise ValueError(f"Value {value} is too low! Minimum: {self.minimum}")
        if self.maximum is not None and value > self.maximum:
            raise ValueError(f"Value {value} is too high! Maximum: {self.maximum}")
        instance._attr = value
    def __delete__(self, instance):
        del instance._attr

class Test:
    attr = RangeValue(0, 100)

t = Test()
t.attr = 50
print(t.attr)

输出:

50

当属性值处于给定范围之外时,赋值将会抛出异常:

class Test:
    attr = RangeValue(0, 100)

t = Test()
t.attr = 150

输出:

ValueError: Value 150 is too high! Maximum: 100

计算属性

描述符可以用于实现一些高级属性的逻辑。例如,计算一个矩形的面积和周长:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    @property
    def area(self):
        return self.width * self.height
    @property
    def perimeter(self):
        return 2 * (self.width + self.height)

class Area:
    def __get__(self, instance, owner):
        return instance.width * instance.height

class Perimeter:
    def __get__(self, instance, owner):
        return 2 * (instance.width + instance.height)

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    area = Area()
    perimeter = Perimeter()

r = Rectangle(3, 4)
print(r.area)
print(r.perimeter)

输出:

12
14

总结

Python描述符是实现高级属性逻辑的重要机制。通过描述符,我们可以实现对属性访问、赋值和删除的控制,以及实现一些高级属性的逻辑。在实际开发中,描述符可以应用于多种场景,例如,实现访问控制、属性校验和计算属性等。