如何在派生类中调用被重写的基类属性?

106 投票
7 回答
65853 浏览
提问于 2025-04-15 12:23

我正在把我的一些类从大量使用获取器和设置器,改成更符合Python风格的属性使用。

但是现在我遇到了困难,因为我之前的一些获取器或设置器会调用基类中对应的方法,然后再执行其他操作。但是用属性怎么实现这个呢?怎么才能在父类中调用属性的获取器或设置器呢?

当然,直接调用属性本身会导致无限递归。

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

7 个回答

31

有一种使用 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 的属性获取器:

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

super(B, self).prop

或者在 Python 3 中:

super().prop

这个方法返回的是属性获取器返回的值,而不是获取器本身,但这足以扩展获取器的功能。

在 B 中,访问父类 A 的属性设置器:

到目前为止,我看到的最佳建议是:

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)

一个注意事项:

除非你的属性没有设置器,否则即使你只想改变其中一个的行为,你也必须在 B 中定义设置器和获取器。

71

super() 这个用法可以解决问题:

return super().bar

在 Python 2.x 版本中,你需要使用更复杂的写法:

return super(FooBar, self).bar
115

你可能会认为可以直接调用由属性调用的基类函数:

class FooBar(Foo):

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

虽然这是最明显的尝试,但我想说——这并不奏效,因为 bar 是一个属性,而不是一个可以直接调用的函数。

不过,属性其实就是一个对象,它有一个获取方法来找到对应的属性值:

class FooBar(Foo):

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

撰写回答