Method Resolution Order in case of Base Classes Having different init params

前端 未结 5 1173
刺人心
刺人心 2021-01-20 05:52

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

5条回答
  •  太阳男子
    2021-01-20 06:11

    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.

提交回复
热议问题