How to call a property of the base class if this property is being overwritten in the derived class?
Solution 1
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)
Solution 2
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
Solution 3
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.
Solution 4
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
Solution 5
Some small improvements to Maxime's answer:
- Using
__class__
to avoid writingB
. Note thatself.__class__
is the runtime type ofself
, but__class__
withoutself
is the name of the enclosing class definition.super()
is a shorthand forsuper(__class__, self)
. - Using
__set__
instead offset
. The latter is specific toproperty
s, 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)
UncleZeiv
Yet another geeky guy with a thing about code, graphics and trains.
Updated on July 08, 2022Comments
-
UncleZeiv almost 2 years
I'm changing some classes of mine from an extensive use of getters and setters to a more pythonic use of properties.
But now I'm stuck because some of my previous getters or setters would call the corresponding method of the base class, and then perform something else. But how can this be accomplished with properties? How to call the property getter or setter in the parent class?
Of course just calling the attribute itself gives infinite recursion.
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
-
Aaron Maenpaa almost 15 yearsTypeError: super() takes at least 1 argument (0 given)
-
shylent almost 15 yearsI guess (well, judging by the link :P), this answer is python3-related. In python3 super() can take zero arguments, yes.
-
Pankrat almost 15 yearssuper() works without arguments in Python 3 and is equivalent to super(MyClass, self) but more readable in my book.
-
akaihola over 11 yearsyou are: he's talking about properties, not plain methods
-
akaihola over 11 yearsThis works fine if you write the base class. But what if you extend a third-party base class which uses the same name for the property and the getter?
-
nerdoc over 10 yearsWhy should I call
Foo.bar.fset(self, c)
in an inherited setter? Why notFoo.bar.fset(c)
- without the "self" -I thought this is implicitely passed? -
hithwen over 10 yearsI get a TypeError: 'property' object is not callable
-
hithwen over 10 yearsIn python 2.7 I get AttributeError: 'super' object has no attribute 'bar'
-
Rob Smallshire over 10 yearssuper().bar seems to work fine for the getter, but doesn't work for assignment through the base property in an overridden setter. If I do super().bar = 3 I get AttributeError: 'super' object has no attribute 'bar'
-
Pankrat over 10 yearsGood point Rob, didn't know that. Here's more information: stackoverflow.com/questions/10810369/…
-
Ethan Furman over 7 yearsWhile this works for getting, it fails for setting with an
AttributeError
. -
Tadhg McDonald-Jensen about 7 years@nerdoc where does the
self
get implies from the chainFoo.bar.fset
? -
nerdoc about 7 yearsJust a thought - AFAIK self is always implicitly passed. If you do a
foo = Foo()
\foo.bar(c)
there is no self passed, but bar() receives it from Python. I'm no Python expert, more or less a beginner too. It's just a thought. -
Sang over 6 yearsHello, this is very helpful. I have a follow-up question to this. This setup works well; however, it stops working when I set init for B to define additional properties. Is there a way to have a separate init for B? Thank you
-
Art over 5 yearsCould you please explain how
super(B, self.__class__)
works exactly withsuper(class, class)
? Where is it documented? -
TallChuck over 5 years
self
is only implicitly passed when the method is called on an instance of a class. For example, if I have a classA
with a methodb(self, arg)
, and I create an instancec = A()
, then callingc.b(arg)
is equivalent toA.b(c, arg)
-
michael over 4 yearsThe above works for the getter. For the setter: super(self.__class__, self.__class__).bar.__set__(self, value) # See bugs.python.org/issue14965
-
Eric about 4 yearsI've suggested some small tweaks to this answer in my answer
-
Matteo Ferla about 4 yearsI thought I'd add in case someone has a reason not to want to call the first base class by name even if it obfuscates the code...
Foo
isself.__class__.__bases__[0]
(handly trick also forsuper(self.__class__.__bases__[1], self)
. -
CharlesB over 3 yearsWhat''s unclear to me is the difference between
super(__class__, self)
andsuper(__class__, self.__class__)
, and why we need the latter for setting property of the ancestor class? -
Eric over 3 yearsFrom the
help()
forsuper
, there are two overloads -super(type, obj)
, which is the first case, andsuper(type, type2)
, which is the second case. We're accessing.prop
as a class attribute not an instance attribute, which is why the latter is needed. -
Will Razen almost 3 years
super(type(self), type(self)).setter.fset(self, value)
doesn't work adequately with multiple inheritance. Try my solutionduper(super()).setter = value
: gist.github.com/willrazen/bef3fcb26a83dffb6692e5e10d3e67ac -
Eric almost 3 yearsIn what case does it not work adequately?
-
Eric almost 3 years
super(Child, Child)
is wrong, it should beSuper(Child, self.__class__)