2012-02-14 50 views
2

所以我有这样的:特殊性覆盖的属性二传手

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会被调用,但是当你稍后作用于已经初始化的对象时,它会调用基础的setter。有人可以解释这里发生了什么吗?

回答

5

因为你称它不一致 - 一次直接,一次通过属性。将Parent.__init__中的self._set_x(x)更改为self.x = x,您将看到Child._set_x从未被调用过。要覆盖在子类中的二传手,你可以使用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_xParent._set_x),并且不会再次查找它们,即使对象的类型发生更改 - 运行的代码无论如何都是相同的。间接强制在self的动态类型中查找,这可以覆盖。

+0

啊,我明白了。因为即使在父母的初始范围内,它仍然会看着设置者的完整的孩子自我。我在想,基类的init方法在没有额外工作的情况下不会找到孩子的setter,但现在我想到了它,当然这是有道理的。谢谢。 – 2012-02-14 19:26:45

2
x = property(_get_x, _set_x) 

这里x成为吸气剂Parent._get_x和setter Parent._set_x的属性。

如果将行x = property(Parent._get_x, _set_x)添加到Child类中,该属性将被重新定义并且将按预期工作。

+0

尽管这只是一个问题。我编辑了这个问题,以消除错误的名称混淆。 – 2012-02-14 19:21:00

0

将方法看作对象。父类的属性x已绑定到父类的_get_x/_set_x方法对象。但是在你的Child类中,你正在调用该类自己的_set_x方法对象。

3

没有不一致。 __init__方法通过属性查找显式调用self._set_xself这里指的是Child对象,并且由于Child定义为_set_x,并且Child类首先位于对象的Method Resolution Order(MRO)中,因此它的版本_set_x是被调用的版本。

x属性在Parent内定义。目前还没有孩子参与,因此版本_set_x和传递给property的版本是Parent中定义的版本。现在,当访问Childx属性时,Python首先在Child类中查找x。但它没有找到它,因为Child没有定义它。然后进入MRO的下一课:Parent。它在那里找到x,并按照Parent中的定义使用它。

+0

是的,你和@Cat Plus Plus有相同的点。谢谢。 :) – 2012-02-14 19:28:46