1

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

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

4

1 回答 1

2

不保证为旧式类调用描述符。从文档

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

所以,这里发生的事情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 中,旧式类被重新实现以使用描述符,这是一个不应该依赖的实现细节。请参阅问题和链接问题

最重要的是,如果您使用描述符,您应该只将它们与新型类一起使用。

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

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}
于 2017-10-02T05:35:53.877 回答