【问题标题】:Why does setter behave differently in new-style class and old-style class为什么 setter 在新式类和旧式类中的行为不同
【发布时间】:2018-03-13 04:31:15
【问题描述】:

使用 python 2.7,假设我有一个 Test 类,其新式类语法定义如下。

class Test(object):
  def __init__(self):
    self._a = 5

  @property
  def a(self):
    return self._a

  @a.setter
  def a(self, val):
    self._a = val

t = Test()
print t.a
t.a = 4
print t.a
print t._a

运行上面的代码将打印5,4,4,这是所需的行为。 但是,如果我将上面代码的第一行更改为class Test:,那么结果将变为5,4,5

有谁知道导致这种输出差异的原因是什么?

【问题讨论】:

    标签: python python-2.7 class new-style-class


    【解决方案1】:

    不保证为旧式类调用描述符。来自docs

    属性访问的默认行为是获取、设置或删除 对象字典中的属性。例如,a.x 有一个 以a.__dict__['x'] 开头的查找链,然后 type(a).__dict__['x'],并继续通过基类 type(a) 不包括元类。如果查找的值是一个对象 定义描述符方法之一,然后 Python 可以覆盖 默认行为并调用描述符方法。这在哪里 出现在优先链中取决于哪些描述符方法 被定义。 请注意,描述符仅针对新样式调用 对象或类(如果类继承自对象或类,则该类是新样式) 类型)。

    所以,这里发生的事情是 Test.a.__set__ 永远不会被调用,您只是将 a 属性添加到 t

    In [8]: class Test:
        ...:   def __init__(self):
        ...:     self._a = 5
        ...:
        ...:   @property
        ...:   def a(self):
        ...:     return self._a
        ...:
        ...:   @a.setter
        ...:   def a(self, val):
        ...:     self._a = val
        ...:
    
    In [9]: t = Test()
    
    In [10]: vars(t)
    Out[10]: {'_a': 5}
    
    In [11]: t.a
    Out[11]: 5
    
    In [12]: t._a
    Out[12]: 5
    
    In [13]: t.a = 100
    
    In [14]: t.a
    Out[14]: 100
    
    In [15]: t._a
    Out[15]: 5
    
    In [16]: vars(t)
    Out[16]: {'_a': 5, 'a': 100}
    

    真正让您感到惊讶的是为什么T.a.__get__ 在这里工作

    答案是,在 Python 2.2 中,旧式类被重新实现以使用描述符,这是一个不应该依赖的实现细节。请参阅this 问题和链接的issue

    底线,如果你使用描述符,你应该只将它们与新式类一起使用。

    注意,如果我确实使用了新样式的类,它应该可以正常工作:

    In [17]: class Test(object):
        ...:   def __init__(self):
        ...:     self._a = 5
        ...:
        ...:   @property
        ...:   def a(self):
        ...:     return self._a
        ...:
        ...:   @a.setter
        ...:   def a(self, val):
        ...:     self._a = val
        ...:
    
    In [18]: t = Test()
    
    In [19]: vars(t)
    Out[19]: {'_a': 5}
    
    In [20]: t.a = 100
    
    In [21]: t.a
    Out[21]: 100
    
    In [22]: t._a
    Out[22]: 100
    
    In [23]: vars(t)
    Out[23]: {'_a': 100}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2012-05-16
      • 2012-02-23
      • 1970-01-01
      • 2018-09-10
      • 2012-11-28
      相关资源
      最近更新 更多