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

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

但是现在我卡住了,因为我以前的一些 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
58369 次浏览

super() should do the trick:

return super().bar

In Python 2.x you need to use the more verbose syntax:

return super(FooBar, self).bar

try

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

Although I'm not sure if python supports calling the base class property. A property is actually a callable object which is set up with the function specified and then replaces that name in the class. This could easily mean that there is no super function available.

You could always switch your syntax to use the property() function though:

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

You might think you could call the base class function which is called by property:

class FooBar(Foo):


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

Though this is the most obvious thing to try I think - it does not work because bar is a property, not a callable.

But a property is just an object, with a getter method to find the corresponding attribute:

class FooBar(Foo):


@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
    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()

(that is unless I am missing something from your explanation)

There is an alternative using super that does not require to explicitly reference the base class name.

Base class 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

In B, accessing the property getter of the parent class A:

As others have already answered, it's:

super(B, self).prop

Or in Python 3:

super().prop

This returns the value returned by the getter of the property, not the getter itself but it's sufficient to extend the getter.

In B, accessing the property setter of the parent class A:

The best recommendation I've seen so far is the following:

A.prop.fset(self, value)

I believe this one is better:

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

In this example both options are equivalent but using super has the advantage of being independent from the base classes of B. If B were to inherit from a C class also extending the property, you would not have to update B's code.

Full code of B extending A's property:

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)

One caveat:

Unless your property doesn't have a setter, you have to define both the setter and the getter in B even if you only change the behaviour of one of them.

Some small improvements to Maxime's answer:

  • Using __class__ to avoid writing B. Note that self.__class__ is the runtime type of self, but __class__ without self is the name of the enclosing class definition. super() is a shorthand for super(__class__, self).
  • Using __set__ instead of fset. The latter is specific to propertys, but the former applies to all property-like objects (descriptors).
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)

You can use the following template:

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)

Note! All of the property methods must be redefined together. If do not want to redefine all methods, use the following template instead:

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)