【问题标题】:Attribute lookup precedence with descriptors and property objects描述符和属性对象的属性查找优先级
【发布时间】:2019-01-26 01:34:51
【问题描述】:

我正在研究描述符以及它们是如何在属性和函数背后使用的机制。当我们实现描述符与使用属性时,我对如何查找属性有点困惑。

class NonDataDescriptor(object):
    def __get__(self, instance, owner):
        return 'non-data descriptor'

class DataDescriptor(object):
    def __get__(self, instance, owner):
        return 'data descriptor'
    def __set__(self, instance, value):
        pass

class MyClass(object):
    descriptor_one = NonDataDescriptor()
    descriptor_two = DataDescriptor()

    def __init__(self):
        self.descriptor_one = 'hello'
        self.descriptor_two = 'goodbye'


mc = MyClass()
print(mc.descriptor_one)
print(mc.descriptor_two)

输出是:

hello
data descriptor

这是有道理的,因为属性查找顺序是:

  1. 数据描述符
  2. instance_obj.__dict__ 中的实例属性
  3. 非数据描述符

由于属性实际上只是实现描述符,我想知道为什么这种属性查找顺序似乎没有得到尊重。请参阅以下内容:

class MyClass(object):
    def __init__(self, fname, lname):
        self._fname = fname
        self._lname = lname

    @property
    def fullname(self):
        return '{} {}'.format(self._fname, self._lname)


mc = MyClass('Bob', 'John')
print(mc.fullname)
mc.__dict__['fullname'] = 'testing'
print(mc.__dict__)
print(mc.fullname)

输出是:

Bob John
{'_fname': 'Bob', '_lname': 'John', 'fullname': 'testing'}
Bob John

我期待,因为属性对象 fullname 实际上只是一个非数据描述符,实例属性 fullname 将优先。

此外,函数也被实现为非数据描述符,并且遵循此属性查找顺序:

class Test:
    def __init__(self):
         self.display = 'display instance attribute'

    def display(self):
        print('display from instance method')

t = Test()
print(t.display)

输出是:

display instance attribute

谁能解释为什么它与属性对象不同?

【问题讨论】:

    标签: python python-3.x function properties


    【解决方案1】:

    @spectras 已经回答了您的问题。我只想提供额外的方法来检查描述符:

    In [517]: import inspect
    
    In [518]: inspect.isdatadescriptor(type(mc).__dict__['fullname'])
    Out[518]: True
    
    In [519]: inspect.isdatadescriptor(MyClass.__dict__['fullname'])
    Out[519]: True
    

    所以,@property 始终是数据描述符,即使您没有定义 setter。在这种情况下,属性分配将引发AttributeError: can't set attribute

    【讨论】:

      【解决方案2】:

      诀窍在于这里的descriptor protocol

      • 如果实例的字典中有一个与数据描述符同名的条目,则数据描述符优先。
      • 如果实例的字典有一个与非数据描述符同名的条目,则字典条目优先。
      @property
      def fullname(self):
          return '{} {}'.format(self._fname, self._lname)
      

      这是一个数据描述符,因为装饰器返回的property对象总是定义__set__,即使你没有定义setter(那么__set__会引发AttributeError,见code here )。

      >>> MyClass.fullname
      <property object at 0x7f30e3917958>
      >>> MyClass.fullname.__set__
      <method-wrapper '__set__' of property object at 0x7f30e3917958>
      

      因此,数据描述符优先。

      由于其他示例的行为已经与您预期的一样,这应该足以完成您的理解。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-08-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多