programowanieProgramista Backend

Jak działa mechanizm kolejności rozwiązywania metod (MRO - Method Resolution Order) w Pythonie przy dziedziczeniu klas? Dlaczego ważne jest zrozumienie kolejności rozwiązywania metod i jak można ją poznać/z изменить?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Kolejność Rozwiązywania Metod (MRO) — to mechanizm w Pythonie, który określa, w jakiej kolejności klasy będą przeszukiwane w celu znalezienia metod i atrybutów w przypadku dziedziczenia wielokrotnego.

C3-linarnizacja (C3 linearization) — algorytm, który jest stosowany do tworzenia kolejności rozwiązywania metod. Zrozumienie MRO jest krytycznie ważne podczas projektowania złożonej hierarchii klas, aby uniknąć niejasności i nieoczekiwanego zachowania metod.

Można poznać MRO dla dowolnej klasy za pomocą atrybutu __mro__ lub funkcji mro():

class A: def hello(self): print('A') class B(A): def hello(self): print('B') class C(A): def hello(self): print('C') class D(B, C): pass print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) obj = D() obj.hello() # 'B', ponieważ B jest przed C w MRO

Nie można jawnie zmieniać MRO, ale można kontrolować kolejność wymieniania klas bazowych.

Pytanie z podstępem

W jakiej kolejności będą wywoływane metody, jeśli klasa dziedziczy od kilku rodziców? Na przykład:

class Base: def foo(self): print('Base') class Left(Base): def foo(self): print('Left') super().foo() class Right(Base): def foo(self): print('Right') super().foo() class Child(Left, Right): pass Child().foo()

Wielu ludzi oczekuje, że "foo" zostanie wywołane najpierw z Left, potem z Right, a następnie z Base — co się zgadza. Jednak jeśli zmienimy kolejność klas bazowych (class Child(Right, Left)), kolejność się zmieni!

Prawidłowa odpowiedź:

Kolejność wywołań będzie taka, jak określono w MRO. W pierwszym przypadku — Left, Right, Base. MRO jest budowane według określonego algorytmu (C3-linarnizacja):

[Child, Left, Right, Base, object]

Przykłady rzeczywistych błędów z powodu braku znajomości szczegółów tematu


Historia

Wielokrotne dziedziczenie logiki, nadpisywanie metody

Zespół deweloperów używał wielokrotnego dziedziczenia dla mixinów. Okazało się, że z powodu niewłaściwej kolejności klas bazowych, wspólna logika mixina została "przykryta" przez klasę potomną, a część logiki biznesowej nie była wykonywana, co prowadziło do utraty danych.


Historia

Wywoływana nie ta metoda rodzica

W metodzie finalizującej (__del__) oczekiwano, że wywołana zostanie metoda klasy bazowej. Jednak z powodu błędnego zrozumienia MRO, wywołano inną, odziedziczoną metodę mixina, w której nie było czyszczenia zasobów. W rezultacie — wyciek pamięci.


Historia

Użycie super() bez zrozumienia łańcucha wywołań

W projekcie Python 3 zamawiający przepisał część kodu z wielokrotnym dziedziczeniem, zapominając zaktualizować wywołania przez super(). Doprowadziło to do sytuacji niekończącej się rekurencji, gdy metoda ciągle wywoływała samą siebie z powodu błędnej kolejności rozwiązywania metod.