使用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
。
有谁知道导致这种输出差异的原因是什么?
答案 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}