如果该属性在派生类中被覆盖,如何调用基类的属性?

Posted

技术标签:

【中文标题】如果该属性在派生类中被覆盖,如何调用基类的属性?【英文标题】:How to call a property of the base class if this property is being overwritten in the derived class? 【发布时间】:2010-11-04 12:31:14 【问题描述】:

我正在将我的一些类从广泛使用 getter 和 setter 更改为更 Pythonic 使用属性。

但现在我被卡住了,因为我以前的一些 getter 或 setter 会调用基类的相应方法,然后执行其他操作。但是如何通过属性来实现呢?如何调用父类中的属性getter或setter?

当然只是调用属性本身会产生无限递归。

class Foo(object):

    @property
    def bar(self):
        return 5

    @bar.setter
    def bar(self, a):
        print a

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return self.bar # --> recursion!

    @bar.setter
    def bar(self, c):
        # perform the same action
        # as in the base class
        self.bar = c    # --> recursion!
        # then do something else
        print 'something else'

fb = FooBar()
fb.bar = 7

【问题讨论】:

【参考方案1】:

您可能认为您可以调用由属性调用的基类函数:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar(self)

虽然这是我认为最明显的尝试 - 它不起作用,因为 bar 是一个属性,而不是可调用的。

但是属性只是一个对象,用getter方法来查找对应的属性:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar.fget(self)

【讨论】:

为什么我应该在继承的 setter 中调用Foo.bar.fset(self, c)?为什么不Foo.bar.fset(c) - 没有“自我” - 我认为这是隐含传递的? 我得到一个 TypeError: 'property' object is not callable @nerdoc 从Foo.bar.fset 链中得到的self 暗示在哪里? 只是一个想法 - AFAIK self 总是被隐式传递。如果您执行 foo = Foo() \ foo.bar(c) 没有传递 self,但 bar() 从 Python 接收它。我不是 Python 专家,或多或少也是初学者。这只是一个想法。 self 仅在对类的实例调用该方法时才隐式传递。例如,如果我有一个类A 和一个方法b(self, arg),并且我创建了一个实例c = A(),那么调用c.b(arg) 就相当于A.b(c, arg)【参考方案2】:

Super 应该可以解决问题:

return super().bar

在 Python 2.x 中,您需要使用更详细的语法:

return super(FooBar, self).bar

【讨论】:

TypeError: super() 至少需要 1 个参数(给定 0) 我猜(嗯,从链接判断:P),这个答案是 python3 相关的。在 python3 中 super() 可以接受零个参数,是的。 super().bar 似乎适用于 getter,但不适用于通过覆盖 setter 中的基本属性进行分配。如果我做 super().bar = 3 我得到 AttributeError: 'super' object has no attribute 'bar' 好点 Rob,不知道。这里有更多信息:***.com/questions/10810369/… 虽然这适用于获取,但无法使用 AttributeError 进行设置。【参考方案3】:

还有一个使用 super 的替代方法,它不需要显式引用基类名称。

基类 A:

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

    @property
    def prop(self):
        return self._prop

    @prop.setter
    def prop(self, value):
        self._prop = value

class B(A):
    # we want to extend prop here
    pass

在B中,访问父类A的属性getter:

正如其他人已经回答的那样,它是:

super(B, self).prop

或者在 Python 3 中:

super().prop

这会返回属性的 getter 返回的值,而不是 getter 本身,但它足以扩展 getter。

在B中,访问父类A的属性setter:

到目前为止我看到的最好的建议如下:

A.prop.fset(self, value)

我相信这个更好:

super(B, self.__class__).prop.fset(self, value)

在此示例中,两个选项是等效的,但使用 super 的优点是独立于 B 的基类。如果B 继承自同样扩展属性的C 类,则无需更新B 的代码。

B扩展A属性的完整代码:

class B(A):
    @property
    def prop(self):
        value = super(B, self).prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(B, self.__class__).prop.fset(self, value)

一个警告:

除非您的属性没有 setter,否则您必须在 B 中同时定义 setter 和 getter,即使您只更改其中一个的行为。

【讨论】:

您好,这很有帮助。我对此有一个后续问题。此设置运行良好;但是,当我为 B 设置 init 以定义其他属性时,它停止工作。有没有办法为 B 设置一个单独的 init ?谢谢 您能否解释一下super(B, self.__class__) 如何与super(class, class) 完全配合使用?它记录在哪里? 我在my answer 中建议对此答案进行一些小调整【参考方案4】:

试试

@property
def bar:
    return super(FooBar, self).bar

虽然我不确定python是否支持调用基类属性。属性实际上是一个可调用对象,它使用指定的函数进行设置,然后在类中替换该名称。这很可能意味着没有可用的超级函数。

您总是可以切换语法以使用 property() 函数:

class Foo(object):

    def _getbar(self):
        return 5

    def _setbar(self, a):
        print a

    bar = property(_getbar, _setbar)

class FooBar(Foo):

    def _getbar(self):
        # return the same value
        # as in the base class
        return super(FooBar, self)._getbar()

    def bar(self, c):
        super(FooBar, self)._setbar(c)
        print "Something else"

    bar = property(_getbar, _setbar)

fb = FooBar()
fb.bar = 7

【讨论】:

如果您编写基类,这可以正常工作。但是,如果您扩展一个使用相同名称的属性和 getter 的第三方基类呢?【参考方案5】:

对Maxime's answer的一些小改进:

使用__class__ 避免写入B。请注意,self.__class__self 的运行时类型,但 __class__ without self 是封闭类定义的名称。 super()super(__class__, self) 的简写。 使用__set__ 代替fset。后者特定于propertys,但前者适用于所有类似属性的对象(描述符)。
class B(A):
    @property
    def prop(self):
        value = super().prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(__class__, self.__class__).prop.__set__(self, value)

【讨论】:

我不清楚super(__class__, self)super(__class__, self.__class__) 之间的区别,以及为什么我们需要后者来设置祖先类的属性? help()super,有两个重载——super(type, obj),第一种情况,super(type, type2),第二种情况。我们将.prop 作为类属性而不是实例属性访问,这就是需要后者的原因。 super(type(self), type(self)).setter.fset(self, value) 不适用于多重继承。试试我的解决方案duper(super()).setter = value:gist.github.com/willrazen/bef3fcb26a83dffb6692e5e10d3e67ac 什么情况下不能正常工作?【参考方案6】:

您可以使用以下模板:

class Parent():
    def __init__(self, value):
        self.__prop1 = value

    #getter
    @property
    def prop1(self):
        return self.__prop1

    #setter
    @prop1.setter
    def prop1(self, value):
        self.__prop1 = value

    #deleter
    @prop1.deleter
    def prop1(self):
        del self.__prop1
  
class Child(Parent):

    #getter
    @property
    def prop1(self):
        return super(Child, Child).prop1.__get__(self)

    #setter
    @prop1.setter
    def prop1(self, value):
        super(Child, Child).prop1.__set__(self, value)

    #deleter
    @prop1.deleter
    def prop1(self):
        super(Child, Child).prop1.__delete__(self)

注意!所有属性方法必须一起重新定义。如果不想重新定义所有方法,请改用以下模板:

class Parent():
    def __init__(self, value):
        self.__prop1 = value

    #getter
    @property
    def prop1(self):
        return self.__prop1

    #setter
    @prop1.setter
    def prop1(self, value):
        self.__prop1 = value

    #deleter
    @prop1.deleter
    def prop1(self):
        del self.__prop1


class Child(Parent):

    #getter
    @Parent.prop1.getter
    def prop1(self):
        return super(Child, Child).prop1.__get__(self)

    #setter
    @Parent.prop1.setter
    def prop1(self, value):
        super(Child, Child).prop1.__set__(self, value)

    #deleter
    @Parent.prop1.deleter
    def prop1(self):
        super(Child, Child).prop1.__delete__(self)

【讨论】:

super(Child, Child)错了,应该是Super(Child, self.__class__)【参考方案7】:
    class Base(object):
      def method(self):
        print "Base method was called"

    class Derived(Base):
      def method(self):
        super(Derived,self).method()
        print "Derived method was called"

    d = Derived()
    d.method()

(除非我在你的解释中遗漏了什么)

【讨论】:

你是:他在谈论属性,而不是简单的方法

以上是关于如果该属性在派生类中被覆盖,如何调用基类的属性?的主要内容,如果未能解决你的问题,请参考以下文章

c++继承是如何工作的?

虚函数的调用

19.面向对象(继承)

java中私有的属性、静态成员可以被子类继承吗?

pytthon3 ---类的属性方法封装继承及小实例

如何在具有相同函数名的派生类中调用基类的函数[重复]