Python __mro__
Published: 2022-01-10 20:17:29

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.

```python
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

```python
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.

```python
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 posicionadas`Child(Parent, Parent2)` 

> essa ordem fica disponível no atributo `__bases__` de sua respectiva classe, por exemplo a classe Child acima:
> ```python
> 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"_

![Python Method Resolution Order inconsistente](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snf8ix687m19yrg7bz04.png)

no código acima não é possivel criar um Method Resolution Order consistente porque estamos tentando passar uma Superclass na frente de uma Subclass.

![Ilustação Method Resolution Order inconsistente](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/frmt9i20zz9a9cs75ypo.png)

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.

```python
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`. 

```python
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.