El Orden de Resolución de Métodos (MRO) es un mecanismo en Python que determina en qué orden se buscan las clases al buscar métodos y atributos en caso de herencia múltiple.
La linealización C3 (C3 linearization) es el algoritmo que se utiliza para construir la secuencia de resolución de métodos. Comprender el MRO es críticamente importante al diseñar una jerarquía de clases compleja, para evitar ambigüedades y comportamientos inesperados de los métodos.
Se puede averiguar el MRO de cualquier clase utilizando el atributo __mro__ o la función 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', porque B aparece antes que C en MRO
No se puede alterar el MRO explícitamente, pero se puede controlar el orden en que se enumeran las clases base.
¿En qué orden se llamarán los métodos si la clase hereda de varios padres? Por ejemplo:
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()
Muchos esperan que "foo" se llame desde Left, luego desde Right, y luego desde Base — así es. Sin embargo, si se cambia el orden de las clases base (class Child(Right, Left)), ¡el orden cambiará!
Respuesta correcta:
El orden de las llamadas se determinará como se define en el MRO. En el primer caso — Left, Right, Base. El MRO se construye mediante un algoritmo específico (linealización C3):
[Child, Left, Right, Base, object]
Historia
Herencia múltiple de lógica, sobreescribiendo un método
El equipo de desarrolladores utilizó herencia múltiple para mixins. Resultó que, debido al orden incorrecto de las clases base, la lógica compartida del mixin fue "sobrescrita" por la clase hija, y parte de la lógica empresarial no se ejecutaba, lo que provocaba la pérdida de datos.
Historia
Se llama al método incorrecto del padre
En el método finalizador (__del__), se esperaba que se llamara al método de la clase base. Sin embargo, debido a una comprensión incorrecta del MRO, se llamó a otro método heredado del mixin, donde no se realizaba la limpieza de recursos. Como resultado, hubo una fuga de memoria.
Historia
Uso de super() sin entender la cadena de llamadas
En un proyecto de Python 3, el cliente reescribió parte del código con herencia múltiple, olvidando actualizar las llamadas a través de super(). Esto llevó a situaciones de recursión infinita, donde el método constantemente se llamaba a sí mismo debido a un orden erróneo de resolución de métodos.