Python __mro__
Joao Vitor Veras
Posted on January 10, 2022
Method Resolution Order ou (MRO) é um que atributo define a ordem em que classes são organizadas, para que os métodos sejam procurados e executados. Em linguagens que não permitem herdar múltiplas classes o MRO não é tão necessário, mas nas que permitem (como Python) ele tem um papel importante.
Quando herdamos uma classe a classe herdada pode ser chamada de Parent ou Superclass e a classe que esta herdando Child ou Subclass, o papel do MRO é definir a ordem das Superclasses em que, será procurado por (e caso encontrado, executado) algum método.
class Grandparent():
def method(self):
return 'grandparent'
class Parent(Grandparent):
def method(self):
return 'parent'
class Child(Parent):
pass
child = Child()
print(child.method()) # > 'parent'
um exemplo bem simples (sem fazer o uso de múltiplas heranças) é o código acima, temos 3 classes que possuem o mesmo método, o MRO primeiro procura pelo método na classe instanciada que estamos e caso não seja encontrado procura nas Superclasses, dessa forma: Child -> Parent -> Grandparent.
é possível visualizar essa ordem acessando o __mro__
na classe Child
child = Child()
# (<class '__main__.Child'>, <class '__main__.Parent'>, <class '__main__.Grandparent'>, <class 'object'>)
python object class
podemos ver uma classe que não criamos no final
<class 'object'>
. essa é uma classe built-in do python, ela está presente para ser a Superclass de classes que não tenham explicitamente definido classes para herdar. Por isso a vimos no final do__mro__
pois a classe Grandparent não herda nenhuma outra classe.
agora utilizando múltiplas heranças vamos ver o MRO cumprindo o seu papel.
class Grandparent():
def method(self):
return 'grandparent'
class Parent(Grandparent):
def method(self):
return 'parent'
class Parent2(Grandparent):
def method(self):
return 'parent2'
class Child(Parent, Parent2):
pass
child = Child()
print(child.method()) # > 'parent'
print(Child.__mro__)
# (<class '__main__.Child'>, <class '__main__.Parent'>, <class '__main__.Parent2'>, <class '__main__.Grandparent'>, <class 'object'>)
como você pode na classe child temos o controle da ordem em que as Superclasses serão posicionadasChild(Parent, Parent2)
essa ordem fica disponível no atributo
__bases__
de sua respectiva classe, por exemplo a classe Child acima:print(Child.__bases__) # (<class '__main__.Parent'>, <class '__main__.Parent2'>)
no exemplo do código acima já começa a ficar interessante, pois o esperado seria que o código tivesse o seguinte MRO
Child -> Parent -> Grandparent -> Parent2 -> Grandparent
mas isso não acontece porque como estamos lidando com herança a precedência importa, isso é, a classe Parent2
tem prioridade sobre a Grandparent
, então o MRO define a seguinte ordem
Child -> Parent -> Parent2 -> GrandParent
então podemos concluir que Subclasses precedem Superclasses (como já dito ali em cima Subclasses, Superclasses = Child, Parent) e o nosso MRO se baseia na ordem do atributo __bases__
Exemplo 2
Se por acaso você tentar criar uma ordem que o MRO não consiga resolver você recebera um TypeError:
"TypeError: Cannot create a consistent method resolution
order (MRO) for bases"
no código acima não é possivel criar um Method Resolution Order consistente porque estamos tentando passar uma Superclass na frente de uma Subclass.
um workaround para esse erro seria invertendo o __bases__
da classe Child Child(Parent3, Parent)
, por mais que funcione existe uma maneira melhor de resolver isso e que vai deixar o seu MRO mais organizado e bem definido e você não terá erros inesperados no futuro.
class Grandparent():
def method(self):
return 'grandparent'
class Parent():
def method(self):
return 'parent'
def parent(self):
return 'parent hierarquicamente'
class Parent2(Parent, Grandparent):
def method(self):
return 'parent2'
class Parent3(Parent2):
pass
class Child(Parent3):
pass
child = Child()
print(child.parent()) # parent hierarquicamente
como temos a acesso a todos os métodos e atributos por hierarquia podemos remover a classe Parent
da classe Child
.
print(Child.__mro__)
# (<class '__main__.Child'>, <class '__main__.Parent3'>, <class '__main__.Parent2'>, <class '__main__.Parent'>, <class '__main__.Grandparent'>, <class 'object'>)
Acima podemos ver como ficou o MRO, bem definido e não trara problemas no futuro.
Posted on January 10, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.