python - 使用类名在Python中设置描述符属性的值

标签 python python-3.x python-descriptors

我试图了解描述符在 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.namejane.name = 'Janny' 作为 name 引用的描述符在类 Person 的主体,即它存在于 Person.__dict__ 上(它是一个 mappingproxy 对象,并且是只读的)。接下来,Person.namePerson.__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/

相关文章:

python - 带有描述符的类型提示

java - jython多线程

python - 通过 ctypes 将 C 数组传递给 python

python - 我怎样才能让这个简单的Python登录程序循环起来?

python - 为什么我的函数要修改输入变量?

python - 编写非数据描述符

python - Z3py:打印包含 144 个变量的大型公式

python - 在没有 '-it' 的情况下运行容器时只出现一行 SimpleHTTPServer 输出

python - brew 在哪里安装 Python 头文件?

python - 试图掌握 clang\cindex.py 中的 CachedPropery