我试图了解描述符在 Python 中的工作原理。我有下面的代码片段。
>>> class D(object):
def __get__(*_):
print('get called')
def __set__(*_):
print('set called')
>>>
>>> class Person(object):
name = D()
>>>
>>> jane = Person()
>>> jane.name
get called
>>> jane.name = 'Janny'
set called
>>> Person.name
get called
>>> Person.name = 'Jack'
>>>
当使用类名访问描述符属性name
时,即调用Person.name
get方法。但是,当我尝试使用相同的方式设置值时,即 Person.name = 'Jack'
设置不会被调用。在这里,我期待着 set 被调用。
我无法理解这种行为。
我使用的是 Python 3.8。
最佳答案
装饰器是在对象的类型上定义的,例如在对象 jane
的示例中,name
引用的描述符必须以 jane
的类型定义,即在类 的主体上定义人
。因此,对于类,需要在类的元类中定义描述符(因为类是其元类的实例),以便为类对象上的给定属性查找调用描述符。
在您的示例中,jane.name
和 jane.name = 'Janny'
作为 name
引用的描述符在类 Person
的主体,即它存在于 Person.__dict__
上(它是一个 mappingproxy
对象,并且是只读的)。接下来,Person.name
在 Person.__dict__
上找到了属性,并发现它具有 __get__
属性,因此它是一个描述符,因此称为 >__get__
用于检索值。现在,当您设置 Person.name = 'Jack'
时,它会将 name
属性设置为通过 Person 引用字符串对象
,删除之前对描述符 Jack
.__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
关于python - 使用类名在Python中设置描述符属性的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58779199/