Почему в этом MRO напечатано «Y» вместо «X»?

#python #method-resolution-order

#питон #метод-разрешение-порядок

Вопрос:

Может кто-нибудь объяснить, почему этот код печатает Y, а не X? Я ожидал, что он напечатает «X», потому что там написано, что он проходит в классе C, а класс X является следующим суперклассом.

 class X:
def foo(self):
    return "X"

class Y:
    def foo(self):
        return "Y"

class A(X):
    def foo(self):
        return self.met()

class B(A):
    def foo(self):
        return "B"

class C(X):
    pass

class D(C, X):
    def met(self):
        return "D"

class E(A, D):
    def foo(self):
        return super().foo()

class F(Y,B):
    pass

class G(D, B):
    pass

class H(E, A, X):
     def met(self):
        return "H"

class I(G,F):
    pass
print(I().foo())
 

Извините за длинный код, но я не знаю, как сделать его короче, не делая вопрос неясным

Комментарии:

1. Чтобы сделать ваш вопрос короче и понятнее, я хотел бы предложить вам проверить цепочку MRO, например, с помощью отладочных отпечатков, таких как print(CLS.__mro__) . Если вы обнаружите что-то конкретное, что не соответствует вашим ожиданиям, сделайте это главным пунктом вопроса.

2. конкретный момент, который не соответствует моим ожиданиям, заключается в том, что после класса C в MRO появляется F, но, насколько я понимаю, класс X должен идти после C.

3. Статья Википедии о линеаризации C3 объясняет, как выполняется упорядочение.

Ответ №1:

На Python Multiple Inheritance: The Diamond Rule :

  1. Перечислите все базовые классы, следуя классическому правилу поиска, и включите класс несколько раз, если он посещался неоднократно. В приведенном выше примере список посещенных классов равен [D, B, A, C, A] .
  2. Просканируйте список на наличие дублированных классов. Если таковые обнаружены, удалите все вхождения, кроме одного, оставив последнее в списке. В приведенном выше примере список становится [D, B, C, A] после удаления дубликатов.
 m = ['I', 'G', 'D', 'C', 'X', 'object', 'X', 'object', 'B', 'A', 
     'X', 'object', 'F', 'Y', 'object', 'B', 'A', 'X', 'object']
s = set()
custom_mro = [i for i in reversed(m) if not (i in s or s.add(i))][::-1]
original_mro = list(i.__name__ for i in I.__mro__)

assert custom_mro == original_mro
print(custom_mro) #['I', 'G', 'D', 'C', 'F', 'Y', 'B', 'A', 'X', 'object']
 

Y печатается, потому Y что класс указан первым в порядке mro.