Method Resolution Order (MRO) in new-style classes?

后端 未结 4 667
离开以前
离开以前 2020-11-22 10:00

In the book Python in a Nutshell (2nd Edition) there is an example which uses
old style classes to demonstrate how methods are resolved in classic resolution or

4条回答
  •  忘了有多久
    2020-11-22 10:11

    Python's method resolution order is actually more complex than just understanding the diamond pattern. To really understand it, take a look at C3 linearization. I've found it really helps to use print statements when extending methods to track the order. For example, what do you think the output of this pattern would be? (Note: the 'X' is suppose to be two crossing edges, not a node and ^ signifies methods that call super())

    class G():
        def m(self):
            print("G")
    
    class F(G):
        def m(self):
            print("F")
            super().m()
    
    class E(G):
        def m(self):
            print("E")
            super().m()
    
    class D(G):
        def m(self):
            print("D")
            super().m()
    
    class C(E):
        def m(self):
            print("C")
            super().m()
    
    class B(D, E, F):
        def m(self):
            print("B")
            super().m()
    
    class A(B, C):
        def m(self):
            print("A")
            super().m()
    
    
    #      A^
    #     / \
    #    B^  C^
    #   /| X
    # D^ E^ F^
    #  \ | /
    #    G
    

    Did you get A B D C E F G?

    x = A()
    x.m()
    

    After a lot of trial an error, I came up with an informal graph theory interpretation of C3 linearization as follows: (Someone please let me know if this is wrong.)

    Consider this example:

    class I(G):
        def m(self):
            print("I")
            super().m()
    
    class H():
        def m(self):
            print("H")
    
    class G(H):
        def m(self):
            print("G")
            super().m()
    
    class F(H):
        def m(self):
            print("F")
            super().m()
    
    class E(H):
        def m(self):
            print("E")
            super().m()
    
    class D(F):
        def m(self):
            print("D")
            super().m()
    
    class C(E, F, G):
        def m(self):
            print("C")
            super().m()
    
    class B():
        def m(self):
            print("B")
            super().m()
    
    class A(B, C, D):
        def m(self):
            print("A")
            super().m()
    
    # Algorithm:
    
    # 1. Build an inheritance graph such that the children point at the parents (you'll have to imagine the arrows are there) and
    #    keeping the correct left to right order. (I've marked methods that call super with ^)
    
    #          A^
    #       /  |  \
    #     /    |    \
    #   B^     C^    D^  I^
    #        / | \  /   /
    #       /  |  X    /   
    #      /   |/  \  /     
    #    E^    F^   G^
    #     \    |    /
    #       \  |  / 
    #          H
    # (In this example, A is a child of B, so imagine an edge going FROM A TO B)
    
    # 2. Remove all classes that aren't eventually inherited by A
    
    #          A^
    #       /  |  \
    #     /    |    \
    #   B^     C^    D^
    #        / | \  /  
    #       /  |  X    
    #      /   |/  \ 
    #    E^    F^   G^
    #     \    |    /
    #       \  |  / 
    #          H
    
    # 3. For each level of the graph from bottom to top
    #       For each node in the level from right to left
    #           Remove all of the edges coming into the node except for the right-most one
    #           Remove all of the edges going out of the node except for the left-most one
    
    # Level {H}
    #
    #          A^
    #       /  |  \
    #     /    |    \
    #   B^     C^    D^
    #        / | \  /  
    #       /  |  X    
    #      /   |/  \ 
    #    E^    F^   G^
    #               |
    #               |
    #               H
    
    # Level {G F E}
    #
    #         A^
    #       / |  \
    #     /   |    \
    #   B^    C^   D^
    #         | \ /  
    #         |  X    
    #         | | \
    #         E^F^ G^
    #              |
    #              |
    #              H
    
    # Level {D C B}
    #
    #      A^
    #     /| \
    #    / |  \
    #   B^ C^ D^
    #      |  |  
    #      |  |    
    #      |  |  
    #      E^ F^ G^
    #            |
    #            |
    #            H
    
    # Level {A}
    #
    #   A^
    #   |
    #   |
    #   B^  C^  D^
    #       |   |
    #       |   |
    #       |   |
    #       E^  F^  G^
    #               |
    #               |
    #               H
    
    # The resolution order can now be determined by reading from top to bottom, left to right.  A B C E D F G H
    
    x = A()
    x.m()
    

提交回复
热议问题