I have classes A, B, and C. C inherits both from A and B, and for the most part wherever there are methods overriding each other I want A's to override B's, so I have the following structure:
class A:
def some_method(self):
return self.some_attribute
class B:
def some_method(self):
return self.some_other_attribute
class C(A,B):
def some_method(self):
var = super().some_method() -> this will give me the method from A, but I want the B one
return var + 1
So my question is how can I invoke with super() or some other way the some_method()
from B, so I can use it in the method override in C?
Also for the record I am using Python 3.*
Python is a language with multiple inheritance, which means that a class can have several direct superclasses. But for the purposes of calling super()
, Python always linearizes the inheritance hierarchy to determine who the "best" superclass is. This is called the method resolution order, accessed via the __mro__
variable in Python.
So if we have the class hierarchy
class A(object): pass
class B(A): pass
class C(A): pass
class D(B, C): pass
Then although D
has two superclassses, if we say super()
in D
, it'll only give us B
. That's because the MRO for D
creates a linear hierarchy for us. In Python 3, it uses an algorithm called the C3 resolution, which gives us this hierarchy
>>> D.__mro__
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
So if we call super()
in a method on class D
, we'll get the version on B
, and if we call super()
from there, we'll get the C
version, then the A
version, and finally the object
one.
So in your case, C
inherits from A
and B
(in that order), so it's going to be biased toward A
. That is, the MRO for the class C
in your example is (C, A, B, object)
. If you want it to always go for B
and then A
, then simply switch the order of the superclasses.
class C(B, A):
...
This is the best solution. It's predictable and Python programmers will immediately understand the intention of a super()
call in this class. I recommend this if it fits your needs.
However, if you have a complicated multi-inheritance system and need access to both the A
and B
superclasses at the same time, you can always call them directly with a class name, forgoing the super()
call altogether.
class C(B, A):
def some_method(self):
A.some_method(self) # Calls A's version on self
B.some_method(self) # Calls B's version on self
If some_method
takes some arguments, you can pass them after the explicit self
argument. This is going to raise more eyebrows, so if you find yourself needing access to two distinct superclass methods in complicated ways, you might consider refactoring your code to use less multiple inheritance. But the feature is there if you really need it.