【问题标题】:Setting values of descriptor attribute in Python using class name使用类名在 Python 中设置描述符属性的值
【发布时间】:2019-11-09 12:16:43
【问题描述】:

我试图了解描述符在 Python 中的工作原理。我有以下代码sn-p。

>>> 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.nameget方法。但是,当我尝试使用相同的方式设置值时,即 Person.name = 'Jack' set 不会被调用。在这里,我期待 set 被调用。

我无法理解这种行为。

我正在使用 Python 3.8。

【问题讨论】:

    标签: python python-3.x python-descriptors


    【解决方案1】:

    装饰器是在对象的类型中定义的,例如在对象jane 的示例中,name 引用的描述符必须以jane 的类型定义,即在类Person 的主体上。因此,在类的情况下,需要在类的元类中定义描述符(因为类是其元类的实例),以便调用描述符以在类对象上查找给定的属性。

    在您的示例中,jane.namejane.name = 'Janny' 用作 name 引用的描述符在类 Person 的主体中定义,即它存在于 Person.__dict__ (这是一个 mappingproxy 对象和是只读的)。接下来,Person.namePerson.__dict__ 上找到属性,并看到它具有__get__ 属性,因此是一个描述符,因此称为__get__ 用于检索值。现在,当您设置Person.name = 'Jack' 时,它会将name 属性设置为通过Person.__setattr__ 引用字符串对象Jack,删除之前对描述符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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-24
      • 2018-01-20
      • 1970-01-01
      相关资源
      最近更新 更多