装饰器是在对象的类型中定义的,例如在您的 object 示例中jane
,所引用的描述符name
必须在jane
class 的主体上定义为 ie 的类型Person
。因此,在类的情况下,需要在类的元类中定义描述符(因为类是其元类的实例),以便为类对象上的给定属性查找调用描述符。
在您的示例中,jane.name
并且jane.name = 'Janny'
作为所引用的描述符name
在类的主体中定义,Person
即它存在于Person.__dict__
(这是一个mappingproxy
对象并且是只读的)上。接下来,Person.name
在 上找到该属性Person.__dict__
并看到它具有该__get__
属性,因此是一个描述符,因此称为__get__
检索值。现在,当您设置 时Person.name = 'Jack'
,它会将属性设置为通过name
引用字符串对象,删除之前对描述符的引用。所以现在所有对eg 的引用,如果你做/ ,你会得到字符串。Jack
Person.__setattr__
D
name
jane.name
Person.name
Jack
以下是如何通过在元类中应用描述符来实现所需行为的示例:
In [15]: class FooDec:
...: def __get__(self, instance, owner=None):
...: print(f'__get__ called with: instance: {instance}, owner: {owner}')
...: def __set__(self, instance, value):
...: print(f'__set__ called with: instance: {instance}, value: {value}')
...:
In [16]: class Meta(type):
...: foo = FooDec()
...:
In [17]: class People(metaclass=Meta):
...: foo = FooDec()
...:
In [18]: People.foo
__get__ called with: instance: <class '__main__.People'>, owner: <class '__main__.Meta'>
In [19]: People.foo = 100
__set__ called with: instance: <class '__main__.People'>, value: 100
In [20]: p = People()
In [21]: p.foo
__get__ called with: instance: <__main__.People object at 0x7faa0b2cb1c0>, owner: <class '__main__.People'>
In [22]: p.foo = 1
__set__ called with: instance: <__main__.People object at 0x7faa0b2cb1c0>, value: 1