为什么setter在新式类和旧式类中的行为不同

时间:2017-10-02 05:11:37

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

使用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

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

1 个答案:

答案 0 :(得分:2)

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

  

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

所以,这里发生的事情是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}