Python super bypass MRO
-
21-12-2019 - |
Question
I have a inherited a class and overwritten a method who also is inherited from a base class. But the thing is that the middle method creates an exception that i would like to bypass by calling the first declared method. Is there a way to specify to the mro that ignores the second calls?
An example could be:
class Base(object):
def __init__(self):
res = "Want this"
print res
class BaseA(Base):
def __init__(self):
res = super(BaseA, self).__init__()
res = "Not this"
print res
class BaseB(BaseA):
def __init__(self):
res = super(BaseB, self).__init()
#At this poing res is "Not this"
#The desire is that it would be "Want this"
print res
Thanks a lot
PD: Something like class BaseB(Base, BaseA) could work?
Solution
Normally you'd fix that method instead.
However, the first argument to super()
is the place to start searching for the next method from. Normally that'd be the current class, but you can also pass in the base class:
class BaseB(BaseA):
def __init__(self):
res = super(BaseA, self).__init__()
Here, super()
takes the MRO of type(self)
, finds BaseA
in that MRO, and looks for the next class implementing __init__
.
Another way to bypass the problematic __init__
method is to just call the unbound method on Base
directly:
class BaseB(BaseA):
def __init__(self):
res = Base.__init__(self)
bypassing any MRO searches entirely.
OTHER TIPS
The Right Way to fix this is to create a new class hierarchy which overrides the offending method with an improved implementation. If you insist on hackery though, this may be what you want:
class BaseB(BaseA):
def __init__(self):
res = super(BaseA, self).__init()
#At this poing res is "Not this"
#The desire is that it would be "Want this"
print res
Note that I'm asking for the super implementation with respect to BaseA, which means that the BaseA implementation is never used.
However, this can do the Wrong Thing when diamond inheritance is involved. Consider:
class Base(object):
def __init__(self):
print 'initing Base'
class BaseA(Base):
def __init__(self):
print 'initing BaseA'
res = super(BaseA, self).__init__()
class BaseB(BaseA):
def __init__(self):
print 'initing BaseB'
res = super(BaseA, self).__init__()
class BaseC(BaseA):
def __init__(self):
print 'initing BaseC'
res = super(BaseC, self).__init__()
class BaseD(BaseB, BaseC):
def __init__(self):
print 'initing BaseD'
res = super(BaseD, self).__init__()
print BaseD()
The output is :
initing BaseD
initing BaseB
initing Base
<__main__.BaseD object at 0x7f1e693a0110>
BaseC
was skipped even though that's not what we wanted. This is because BaseC
came between BaseB
and BaseA
in the method resolution order, so when we skipped from BaseB
to BaseA
we inadvertently neglected BaseC
.
>>> print [cls.__name__ for cls in BaseD.mro()]
['BaseD', 'BaseB', 'BaseC', 'BaseA', 'Base', 'object']
How bout just
Base.__init__(self)