I am trying to understand MRO in Python. Although there are various posts here, I am not particularly getting what I want. Consider two classes A
and B
While bj0's answer is mostly right, manually extracting the arguments from kwargs
is more complicated and awkward than is necessary. It also means that you won't detect when extra arguments are passed in to one of the class constructors.
The best solution is to accept **kwargs
, but only use it to pass on any unknown arguments. When this reaches object
(BaseClass
's base), it will raise an error if there were unnecessary arguments:
class BaseClass(object):
def __init__(self, **kwargs):
super(BaseClass, self).__init__(**kwargs) # always try to pass on unknown args
class A(BaseClass):
def __init__(self, something, anotherthing, **kwargs): # take known arguments
super(A, self).__init__(**kwargs) # pass on the arguments we don't understand
self.something = something
self.anotherthing = anotherthing
class B(BaseClass):
def __init__(self, someOtherThing, **kwargs): # same here
super(B, self).__init__(**kwargs) # and here
self.someOtherThing = someOtherThing
class C(A, B): # this will work, with someOtherThing passed from A.__init__ to B.__init__
pass
class D(B, A): # this will also work, with B.__init__ passing on A.__init__'s arguments
pass
import threading
class E(C, threading.Thread): # keyword arguments for Thread.__init__ will work!
def run(self):
print(self.something, self.anotherthing, self.someOtherThing)
If one of your classes modifies (or provides a default for) an argument that is also used by one of its base classes, you can both take a specific parameter and pass it on by keyword:
class F(C):
def __init__(self, something, **kwargs):
super(F, self).__init__(something="foo"+something, **kwargs)
You do need to be calling all your constructors with only keyword arguments, no positional ones. For instance:
f = F(something="something", anotherthing="bar", someOtherThing="baz")
It's possible to support something similar for positional arguments, but usually its a bad idea because you can't count on the argument order. If you had just one class that took positional arguments (perhaps an unknown number of them in *args
), you could probably make that work by passing *args
into and out of each __init__
method, but multiple classes taking different positional arguments is asking for trouble due to the order they appear in possibly changing as you do multiple inheritance.