【问题标题】:Overriding inherited properties’ getters and setters in Python在 Python 中覆盖继承属性的 getter 和 setter
【发布时间】:2011-03-21 04:32:00
【问题描述】:

我目前正在使用@property 装饰器在我的几个类中实现“getter 和 setter”。我希望能够在子类中继承这些@property 方法。

我有一些 Python 代码(具体来说,我在 py3k 中工作)看起来有点像这样:

class A:
    @property
    def attr(self):
        try:
            return self._attr
        except AttributeError:
            return ''

class B(A):
    @property
    def attr(self):
        return A.attr   # The bit that doesn't work.

    @attr.setter
    def attr(self, value):
        self._attr = value

if __name__ == '__main__':
    b = B()
    print('Before set:', repr(b.attr))
    b.attr = 'abc'
    print(' After set:', repr(b.attr))

我已经用评论标记了不能使用的部分。我希望返回基类的 attr getter。 A.attr 返回一个属性对象(这可能非常接近我的需要!)。

编辑
在收到 Ned 的以下回答后,我想出了一个更优雅的解决方案。

class A:
    @property
    def attr(self):
        try:
            return self._attr
        except AttributeError:
            return ''

class B(A):        
    @A.attr.setter
    def attr(self, value):
        self._attr = value

if __name__ == '__main__':
    b = B()
    print('Before set:', repr(b.attr))
    b.attr = 'abc'
    print(' After set:', repr(b.attr))

.setter 装饰器需要一个属性对象,我们可以使用@A.attr 获得它。这意味着我们不必在子类中再次声明该属性。

(这是在一天结束时解决问题与在一天开始时解决问题之间的区别!)

【问题讨论】:

  • 我遇到了同样的问题,并且可以确认您的答案有效,但是哇,这是一个 $hitty 的“功能” - 添加超类名称链接!这对 python 来说是一个糟糕的设计。要么,要么“重新实现”该属性。两者都是糟糕的继承“解决方案”。
  • 哇!谢谢@A.attr.setter。我先试了@super.attr.setter 这不行...

标签: python properties


【解决方案1】:

我想你想要:

class B(A):
    @property
    def attr(self):
        return super(B, self).attr

你提到想要返回父类的getter,但是你需要调用getter,而不是返回它。

【讨论】:

  • 这对我不起作用,但是,如果我打电话给super().attr,我会得到想要的效果。这是明智的做法吗?
  • 啊,你是对的,因为父级的 .attr 不再可用作可调用的 getter,只能用作属性。 super() 是从父类访问同名属性的方法。为什么这是不明智的做法?
  • 只是检查。我没有反对的理由。将您的回复更新为 super().attr 而不是 super(B, self).attr(),我会将其标记为已回答 :) 感谢您的提示!
  • 你明白了!我会为那个大的绿色复选标记做几乎任何事情! :)
  • 虽然我只使用了super().attr 而不是super(B, self).attr。然而,两者似乎都有效。
【解决方案2】:

为了覆盖 python 2 中的设置器,我这样做了:

class A(object):
    def __init__(self):
        self._attr = None

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value


class B(A):
    @A.attr.setter
    def attr(self, value):
        # Do some crazy stuff with `value`
        value = value[0:3]
        A.attr.fset(self, value)

要了解A.attr.fset 的来源,请参阅property 类的文档: https://docs.python.org/2/library/functions.html#property

【讨论】:

  • 有趣的是,B 没有A 继承getter。如果B 仅继承自object,则B.attr.fget 将返回<function Foo.baz at 0x123456789>。我担心A.attr.fset 现在被分配了一个用户定义的函数。 A.attr.fset 仍然是 None,而 B.attr.fset<function B.baz at 0x987654321>。因此,覆盖任何属性的可访问性成员,首先创建属性的副本。至少,这似乎是正在发生的事情。
  • 我特别喜欢您展示如何仅覆盖 setter。我不知道该怎么做,这让我对 setter 装饰器有了更深入的了解。
  • @TylerCrompton。这就是你必须做@propertyname.setter 的全部原因:属性是不可变的对象,通常,如果你定义了一个getter 和一个setter,你就已经创建了两个属性对象,第一个被丢弃了。我相信您在最初发表评论五年后才意识到这一点,但我会留在这里留给后代。
  • 我经常看到属性getter有get_attr这样的名字,setter有set_attr这样的名字的约定,我遵循这个约定。但是,当我测试您的代码时,它在子类 B(A) 中的 setter 方法与属性同名时才有效。换句话说,如果我在您的代码中将def attr(self, value) 替换为def set_attr(self, value),那么它将停止工作(它不会覆盖父类的属性设置器)。这似乎有点违反直觉。是不是bug?
  • 这行得通。但是,如果我还想覆盖父母的吸气剂,添加类似@A.attr.setter\\def attr(self) 的东西由于某种原因不起作用。如果它出现在@A.attr.setter 部分之后,则忽略设置器部分。反之亦然。为什么?
猜你喜欢
  • 2011-03-11
  • 2013-11-11
  • 2018-11-08
  • 2019-02-28
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多