【问题标题】:Peculiarity of overridden property setter in subclass子类中被覆盖的属性设置器的特殊性
【发布时间】:2012-03-06 04:00:23
【问题描述】:

所以我有这个:

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。有人可以解释这里发生了什么吗?

【问题讨论】:

    标签: python properties overriding


    【解决方案1】:

    因为你调用它的方式不一致——一次是直接调用,一次是通过属性调用。将Parent.__init__ 中的self._set_x(x) 更改为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_xParent._set_x),并且不会再次查找它们,即使对象的类型发生了变化——代码不管怎样,运行都是一样的。间接强制在self 的动态类型中查找,它允许覆盖。

    【讨论】:

    • 啊,我明白了。因为即使在父级init的范围内,它仍然会为setter查看完整的子级self。我在想,如果没有额外的工作,基类的 init 方法不会找到孩子的 setter,但现在我想想,这当然是有道理的。谢谢。
    【解决方案2】:

    没有矛盾。 __init__ 方法通过属性查找显式调用 self._set_xself 在这里指的是一个Child 对象,并且由于Child 定义了_set_x,并且Child 类在对象的Method Resolution Order (MRO) 中是第一个,所以它的_set_x 版本是被调用。

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

    【讨论】:

    • 是的,你和@Cat Plus Plus 达成了一致。谢谢。 :)
    【解决方案3】:
    x = property(_get_x, _set_x)
    

    这里x 变成了一个属性,getter Parent._get_x 和setter Parent._set_x

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

    【讨论】:

    • 这只是引出了问题。我编辑了问题以消除误导性名称混淆。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-28
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 2012-06-03
      • 1970-01-01
      相关资源
      最近更新 更多