How does Python pass __init__ parameters with multiple inheritance

前端 未结 3 1606
余生分开走
余生分开走 2021-01-14 12:52

I have this code, showing a classic diamond pattern:

class A:
    def __init__( self, x ):
        print( \"A:\" + x )


class B( A ):
    def __init__( self         


        
相关标签:
3条回答
  • 2021-01-14 13:24

    Classes in Python are dynamically composed - that includes inheritance.

    The C:b output does not imply that B magically inherits from C. If you instantiate either B or C, none knows about the other.

    >>> B('root')
    B:root
    A:b
    

    However, D does know about both B and C:

    class D(B,C):
        ...
    

    There is a lot of technicalities available on this. However, there are basically two parts in how this works:

    1. Direct Base Classes are resolved in order they appear.
      • B comes before C.
    2. Recursive Base Classes are resolved to not duplicate.
      • A Base Class of both B and C must follow both.

    For the class D, that means the base classes resolve as B->C->A! C has sneaked in between B and A - but only for class D, not for class B.


    Note that there is actually another class involved: all classes derive from object by default.

    >>> D.__mro__
    (__main__.D, __main__.B, __main__.C, __main__.A, object)
    

    You have already written A knowing that there is no base to take its parameters. However, neither B nor C can assume this. They both expect to derive from an A object. Subclassing does imply that both B and C are valid A-objects as well, though!

    It is valid for both B and C to precede B and C, since the two are subclasses of A. B->C->A->object does not break that B expects its super class to be of type A.

    With all other combinations, one ends up with C preceding nothing (invalid) or object preceding something (invalid). That rules out depth-first resolution B->A->object->C and duplicates B->A->object->C->A->object.


    This method resolution order is practical to enable mixins: classes that rely on other classes to define how methods are resolved.

    There is a nice example of how a logger for dictionary access can accept both dict and OrderedDict.

    # basic Logger working on ``dict``
    class LoggingDict(dict):
        def __setitem__(self, key, value):
            logging.info('Settingto %r' % (key, value))
            super().__setitem__(key, value)
    
    # mixin of different ``dict`` subclass
    class LoggingOD(LoggingDict, collections.OrderedDict):
        pass
    
    0 讨论(0)
  • 2021-01-14 13:25

    Python uses the C3 linearization algorithm to establish the method resolution order, which is the same order that super delegates in.

    Basically, the algorithm keeps lists for every class containing that class and every class it inherits from, for all classes that the class in question inherits from. It then constructs an ordering of classes by taking classes that aren't inherited by any unexamined classes one by one, until it reaches the root, object. Below, I use O for object for brevity:

    L(O) = [O]
    
    L(A) = [A] + merge(L(O), [O]) = [A, O]
    
    L(B) = [B] + merge(L(A), [A]) = [B] + merge([A, O], [A]) = [B, A] + merge([O]) 
         = [B, A, O]
    
    L(C) = [C] + merge(L(A), [A]) = [C] + merge([A, O], [A]) = [C, A] + merge([O]) 
         = [C, A, O]
    
    L(D) = [D] + merge(L(B), L(C), [B, C]) = [D] + merge([B, A, O], [C, A, O], [B, C])
         = [D, B] + merge([A, O], [C, A, O], [C]) = [D, B, C] + merge([A, O], [A, O])
         = [D, B, C, A, O]
    
    0 讨论(0)
  • 2021-01-14 13:35

    You can always check the method resolution order that any class should have:

     >>> D.mro()
     [__main__.D, __main__.B, __main__.C, __main__.A, object]
    

    As you can see, if everybody is doing the right thing (i.e. calling super), the MRO will be 1st parent, 2nd parent, 1st parent's parent and so on...

    You can just think of depth first and then left to right to find the order although ever since python 2.3 the algorithm changed but the outcome is usually the same.

    In this case B and C have the same parent A and A doesn't call super

    0 讨论(0)
提交回复
热议问题