Python黑魔法Descriptor描述符的实例解析
Python中,Descriptor
描述符被用作属性的获取、设置和删除时自动触发的一系列协议。通过实现Descriptor
协议,我们可以自定义属性访问的行为,从而实现更加灵活高效的属性操作。
Descriptor
描述符协议
Python对于Descriptor
描述符的协议规定了三个特殊方法:__get__
、__set__
和__delete__
,分别用于在属性被获取、设置和删除时触发。
class Descriptor:
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
其中,__get__(self, instance, owner)
方法用于获取属性值,其中self
为Descriptor
实例本身,instance
为属性所在的实例对象,owner
为属性所在的类对象。如果该属性为类属性,则instance
为None
。
__set__(self, instance, value)
方法用于设置属性值,其中value
为要设置的属性值。
__delete__(self, instance)
方法用于删除属性。
实例1:简单的计数器
我们通过实现一个简单的计数器来演示Descriptor
的应用:
class Counter:
def __init__(self):
self.count = 0
def __get__(self, instance, owner):
return self.count
def __set__(self, instance, value):
self.count = value
在这个实例中,我们实现了一个计数器Counter
类,其中count
为计数器的属性。在__get__
方法中,我们返回了count
属性当前的值。在__set__
方法中,我们将count
属性的值设置为传入的value
参数。
接下来,我们创建一个实例并对其进行访问和修改操作:
class Test:
count = Counter()
t = Test()
print(t.count) # 输出:0
t.count = 10
print(t.count) # 输出:10
我们发现,对于Test
类的count
属性,其实现为一个Descriptor
描述符对象。在访问和设置属性时,实际上是触发了Counter
类中__get__
和__set__
方法。通过这种方式,我们实现了一个可自动计数的属性,进而实现了更加灵活高效的属性操作。
实例2:限制属性值的范围
我们来实现一个Range
描述符,用于限制属性值的取值范围。具体来说,我们可以通过传入一个范围的参数构造Range
实例,在__set__
方法中通过判断属性值是否在该范围内,进而对属性进行限制。
class Range:
def __init__(self, min_value, max_value):
self.min = min_value
self.max = max_value
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if value < self.min or value > self.max:
raise ValueError(f'value should be in range [{self.min}, {self.max}]')
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
在这个实例中,我们实现了一个Range
描述符类,其中min
和max
为范围参数。在__get__
方法中,我们直接返回了属性的值。在__set__
方法中,我们对于传入的value
参数进行了范围判断,并在不符合要求时抛出一个异常。
注意到上述实现中,我们通过__set_name__
方法实现了对name
属性的设置,从而在__set__
方法中可以获取被操作的属性名称。这个方法是在Python 3.6版本中加入的,用于在类实例化时直接对描述符进行绑定。
接下来,我们创建一个Test
类,并使用Range
描述符限制其属性值的范围:
class Test:
age = Range(0, 10)
t = Test()
t.age = 5
print(t.age) # 输出:5
t.age = 11 # 抛出异常:ValueError: value should be in range [0, 10]
我们发现,对于Test
类的age
属性,其实现为一个Range
描述符对象。在设置属性时,实际上是触发了Range
描述符中__set__
方法,并进行了范围判断。通过这种方式,我们实现了一个可限制属性值范围的描述符,并且可以在属性设置时自动进行检查和抛出异常。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python黑魔法Descriptor描述符的实例解析 - Python技术站