所以我有这个:
class Parent(object):
def __init__(self, val):
print 'enter Base init'
self._set_x(val)
print 'leave Base init'
def _get_x(self):
return self._x
def _set_x(self, val):
print 'enter Base _set_x'
self._x = val
print 'leave Base _set_x'
x = property(_get_x, _set_x)
class Child(Parent):
def _set_x(self, val):
print 'enter Child _set_x'
y = val * 2
super(Child, self)._set_x(y)
print 'leave Child _set_x'
child = Child(5)
num = child.x
child.x = 5
print num == child.x
当我运行它时,我明白了:
enter Base init
enter Child _set_x
enter Base _set_x
leave Base _set_x
leave Child _set_x
leave Base init
enter Base _set_x
leave Base _set_x
False
我一直在读书,人们都说最重要的不应该奏效,但我的问题是为什么这里看似不一致?从init调用时,子类'setter被调用,但是当你稍后对已经初始化的对象进行操作时,它会调用base的setter。有人可以解释这里发生了什么吗?
答案 0 :(得分:5)
因为你不一致地称它 - 一次直接称呼,一次通过一个属性。将self._set_x(x)
中的Parent.__init__
更改为self.x = x
,您将看到永远不会调用Child._set_x
。要覆盖子类中的setter,可以使用property.setter
作为装饰器:
class Child(Parent):
@Parent.x.setter
def x(self, arg):
super()._set_x(arg)
或者向属性添加一个间接级别:
class Parent(object):
# ...
x = property(
lambda self: self._get_x(),
lambda self, x: self._set_x(x)
)
覆盖不能直接起作用,因为属性存储具体的方法对象(Parent._get_x
和Parent._set_x
),并且不会再次查找它们,即使对象的类型恰好发生了变化 - 无论如何运行的代码都是相同的。间接强制在动态类型self
中查找,允许覆盖。
答案 1 :(得分:3)
没有矛盾。 __init__
方法通过属性查找显式调用self._set_x
。 self
在此处引用Child
对象,由于Child
定义_set_x
,Child
类位于对象的Method Resolution Order(MRO)中),它的_set_x
版本是被调用的版本。
但是x
属性是在Parent
内定义的。尚未涉及儿童,因此传递给_set_x
的{{1}}和_get_x
版本是property
中定义的版本。现在,当访问Parent
的{{1}}属性时,Python首先在x
类中查找Child
。但它找不到它,因为x
没有定义它。然后它进入MRO的下一课:Child
。它在那里找到Child
,并按照Parent
中的定义使用它。
答案 2 :(得分:2)
x = property(_get_x, _set_x)
此处x
成为具有getter Parent._get_x
和setter Parent._set_x
的属性。
如果您将行x = property(Parent._get_x, _set_x)
添加到Child
类,则会重新定义该属性并按预期工作。
答案 3 :(得分:0)
将方法视为对象。 Parent类上的属性x已绑定到父类的_get_x / _set_x方法对象。但是在你的Child类中,你正在调用该类'自己的_set_x方法对象。