【问题标题】:Is it Possible to rewrite the getter of a property in a subclass?是否可以重写子类中属性的getter?
【发布时间】:2011-05-30 21:14:26
【问题描述】:

例如:

class Example:
    def __init__(self):
        self.v = 0

    @property
    def value(self):
        return self.v
    @value.setter
    def value(self, v):
        self.v = v

class SubExample(Example):
    pass

是否可以仅将 getter 重写为 SubExample 中的值?

【问题讨论】:

  • 我修复了您的代码示例,使其成为有效的 Python。

标签: python inheritance


【解决方案1】:

你可以这样做

class DoubleExample(Example):
    @Example.value.getter
    def value(self):
        return self.v * 2

o = Example()
o.value = 1
print o.value # prints "1"

p = DoubleExample()
p.value = 1
print p.value # prints "2"

但是,这仅在 Example 是新样式类 (class Example(object):) 而不是旧样式类 (class Example:) 时才有效,就像在您的示例代码中一样。


警告:Thomas 在 cmets 中指出,如果您使用多重继承 (class Foo(Bar, Baz)),此方法可能无法按预期运行。

【讨论】:

  • 啊,这就是我想要的。我不知道这种新/旧类风格的东西,我还在学习。谢谢。还有什么地方可以让我了解更多关于使用@的信息吗?
  • Setter 仅适用于新型类(这些类直接或间接继承自 object。)此解决方案的问题是它无法正确处理多重继承情况,在继承层次结构中,其他一些类位于 DoubleExampleExample 之间。
  • @ghasyx:为了帮助您进行搜索,@ 的使用名称是“装饰者”。它们的工作方式非常简单:将@Example.value.getter 放在方法之前就像将value = Example.value.getter(value) 放在它之后。恐怕我不知道有什么好的资源来了解它们。关于新/旧样式类:我认为旧样式类只是为了向后兼容,您应该在编写的代码中始终使用新样式类。
  • @Thomas:你能举一个例子说明这不起作用吗?我对它在多重继承下的行为感到不舒服,但无法提出一个失败的简单案例。
  • 一个 SideExample 继承自 Example 并覆盖该属性,以及一个 DoubleSideExample 继承自 DoubleExampleSideExample(按此顺序),不涉及该属性. DoubleSideExample 类将具有由SideExample 修改的Example 的大部分行为,但该属性将完全忽略SideExample 类。
【解决方案2】:

不可能在子类中覆盖属性的 getter,不。该属性是一个存在于类中的对象,它包含对您为其提供的函数的引用——如果您稍后重新定义这些函数的名称,它根本不会影响该属性。

可以做的是让您的属性调用执行间接调用的函数,如下所示:

class Example(object):
    def __init__(self):
        self.v = 0

    @property
    def v(self):
       return self._v_getter()
    @v.setter
    def v(self, value):
       return self._v_setter(value)

    def _v_setter(self, value):
        self._v = value

class SubExample(Example):
    def _v_getter(self):
        return 5

>>> se = SubExample()
>>> se.v
5
>>> se._v
0
>>> se.v = 10
>>> se.v
5
>>> se._v
10

或者,您可以通过简单地定义一个新属性来重新定义子类中的整个属性。但是,您将无法方便地访问父类中定义的函数或属性,并且在面对多重继承时做正确的事情是很困难的。

【讨论】:

  • 看起来您在 Example.__init__ 中的内部属性名称前错过了下划线?你不应该在你的基类中也定义一个占位符 _v_getter 方法吗?我在一个基类中做到了这一点,其中覆盖属性 getter 是该类的主要用途之一。用户可以将 self.dloc = NPArray 设置为固定值,或者设置 self.calc_loc = self.foo 来覆盖属性 getter 方法。使用对象的代码只是使用 somenode.loc 来请求值。
  • 我没有错过__init__ 中的下划线。 __init__ 方法只是分配给属性,这意味着实际属性的设置器被调用。是的,通常您希望基类也提供最小的_v_getter。我不记得为什么我没有在这里这样做。
  • 对!所有方法在初始化时就已经存在,所以 self.v 甚至在 init 中也会调用 self._v_setter。我觉得这有点棘手是不是很奇怪?我的直接想法是你引用了一个未初始化的变量 _v...
  • 我不确定您为什么认为这很棘手。 __init__ 方法在类的实例被创建时被调用,这是在类本身被创建之后所必需的。
【解决方案3】:

您的问题之前已经回答过:

http://code.activestate.com/recipes/408713-late-binding-properties-allowing-subclasses-to-ove/ http://stackoverflow.com/questions/3393534/python-property-and-method-override-issue-why-subclass-property-still-calls-the

本质上,不是直接使用property,而是必须推迟调用getter,以便它访问子类中定义的getter,而不是第一次定义属性时在超类中定义的getter。这可以通过lambda 来实现:

class Test(object):
    def __init__(self):
        self.v = 0
    def _value(self):
        return self.v
    def _value_setter(self, v):
        self.v = v
    value = property(lambda self: self._value(), lambda self, v: self._value_setter(v))

class Test2(Test):
    def _value(self):
        return self.v + 1

a = Test()
a.value = 2
print a.value  # 2

b = Test2()
b.value = 2
print b.value  # 4

【讨论】:

    猜你喜欢
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 2023-04-09
    相关资源
    最近更新 更多