def mro(*bases): """Calculate the Method Resolution Order of bases using the C3 algorithm. Suppose you intended creating a class K with the given base classes. This function returns the MRO which K would have, *excluding* K itself (since it doesn't yet exist), as if you had actually created the class. Another way of looking at this, if you pass a single class K, this will return the linearization of K (the MRO of K, *including* itself). """ seqs = [list(C.__mro__) for C in bases] + [list(bases)] res = [] while True: non_empty = list(filter(None, seqs)) if not non_empty: # Nothing left to process, we're done. return tuple(res) for seq in non_empty: # Find merge candidates among seq heads. candidate = seq[0] not_head = [s for s in non_empty if candidate in s[1:]] if not_head: # Reject the candidate. candidate = None else: break if not candidate: raise TypeError("inconsistent hierarchy, no C3 MRO is possible") res.append(candidate) for seq in non_empty: # Remove candidate. if seq[0] == candidate: del seq[0] ### if __name__ == '__main__': # Run self-tests. Prints nothing if they succeed. O = object class SeriousOrderDisagreement: class X(O): pass class Y(O): pass class A(X, Y): pass class B(Y, X): pass bases = (A, B) try: x = mro(*SeriousOrderDisagreement.bases) except TypeError: pass else: print("failed test, mro should have raised but got %s instead" % (x,)) class Example0: # Trivial single inheritance case. class A(O): pass class B(A): pass class C(B): pass class D(C): pass tester = D expected = (D, C, B, A, O) class Example1: class F(O): pass class E(O): pass class D(O): pass class C(D, F): pass class B(D, E): pass class A(B, C): pass tester = A expected = (A, B, C, D, E, F, O) class Example2: class F(O): pass class E(O): pass class D(O): pass class C(D, F): pass class B(E, D): pass class A(B, C): pass tester = A expected = (A, B, E, C, D, F, O) class Example3: class A(O): pass class B(O): pass class C(O): pass class D(O): pass class E(O): pass class K1(A, B, C): pass class K2(D, B, E): pass class K3(D, A): pass class Z(K1, K2, K3): pass assert mro(A) == (A, O) assert mro(B) == (B, O) assert mro(C) == (C, O) assert mro(D) == (D, O) assert mro(E) == (E, O) assert mro(K1) == (K1, A, B, C, O) assert mro(K2) == (K2, D, B, E, O) assert mro(K3) == (K3, D, A, O) tester = Z expected = (Z, K1, K2, K3, D, A, B, C, E, O) for example in [Example0, Example1, Example2, Example3]: # First test that the expected result is the same as what Python # actually generates. assert example.expected == example.tester.__mro__ # Now check the calculated MRO. assert mro(example.tester) == example.expected