11.1 Method Resolution Order
A Ordem de Resolução de Métodos (Method Resolution Order, MRO) determina a sequência em que o Python busca métodos e atributos na hierarquia de classes. Isso é especialmente importante ao trabalhar com múltipla herança, onde uma classe pode herdar atributos e métodos de várias classes pai.
Basicamente, existe uma ordem fixa e rigorosa (ou melhor, um algoritmo), segundo o qual o Python percorre a árvore de herança das classes. Esse algoritmo garante a correta ordem de busca dos métodos, que pode ser descrita assim:
Algoritmo de Linearização C3
O Algoritmo de Linearização C3 determina o MRO combinando:
- A própria classe.
- Lista de classes pai na ordem que são listadas.
- MRO das classes pai na mesma ordem.
Regras do Algoritmo de Linearização C3
- Preservar a ordem local dos métodos: se a classe
Aé mencionada antes da classeB, todos os métodos da classeAdevem ser considerados antes dos métodos da classeB. - Manter a ordem nas classes pai: se a classe
Aé pai da classeB, então todos os métodos da classeAdevem ser considerados antes dos métodos da classeB. - Levar em conta a ordem de herança: se a classe
Cé pai de duas ou mais classes, a ordem dos métodos da classeCdeve ser mantida no MRO de todas essas classes.
Passos do algoritmo:
Passo 1. Começamos com a própria classe:
Sempre começamos com a própria classe onde o método é chamado.
Passo 2. Adicionamos as classes base na ordem que são listadas:
Depois da classe atual, verificamos as classes base na ordem em que são especificadas na herança.
Passo 3. Percorremos as classes pai:
Procuramos campos e métodos lá.
Passo 4. Combinamos o MRO das classes pai:
Se a mesma classe base for herdada através de múltiplos caminhos, ela será verificada apenas uma vez e na ordem correta (todas as outras vezes será pulada).
Para aqueles que já estão familiarizados com o assunto "Algoritmos e Estruturas de Dados", isso é busca em profundidade e não em largura.
11.2 Verificando o MRO
No Python, é possível verificar a ordem de busca de métodos e campos da classe usando o atributo __mro__ ou a função mro().
Exemplo:
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
def method(self):
print("D")
# Verificando MRO
print(D.__mro__)
A saída será:
(<class '__main__.D'>,
<class '__main__.B'>,
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
Isso mostra a ordem em que o Python buscará métodos e atributos:
D: O Python checa primeiro o método na classeD.-
B: Depois, o Python checa o método na classeB(primeira classe pai). -
C: Se o método não for encontrado na classeB, o Python checa o método na classeC(segunda classe pai). -
A: Se o método não for encontrado nas classesBeC, o Python verifica o método na classeA. -
object: Finalmente, o Python verifica o método na classe baseobject.
11.3 Usando super() com MRO
A função super() segue o MRO para chamar métodos de classes pai na ordem correta. Considere o exemplo de uso de
super():
class A:
def method(self):
print("A")
super().method()
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
d = D()
d.method()
A saída será a seguinte:
D
B
C
A
Ordem de Busca (MRO)
1. Chamada do método method da classe D:
- O Python checa primeiro o método na classe
De o encontra lá. - O método
D.method()é executado e imprime"D". - Então,
super().method()é chamado, que segue o MRO para chamar o próximo método.
2. Chamada do método method da classe B:
- De acordo com o MRO, a próxima classe após
DéB. - O método
B.method()é executado e imprime"B". - Então,
super().method()é chamado, que segue o MRO para chamar o próximo método.
3. Chamada do método method da classe C:
- A próxima classe no MRO após
BéC. - O método
C.method()é executado e imprime"C". - Então,
super().method()é chamado, que segue o MRO para chamar o próximo método.
4. Chamada do método method da classe A:
- A próxima classe no MRO após
CéA. - O método
A.method()é executado e imprime"A". - Então,
super().method()é chamado, mas comoAnão tem métodos pai demethod(além deobject), a chamada termina sem mais ações.
GO TO FULL VERSION