12.1 Problema Do Diamante
O exemplo anterior pode parecer uma boa maneira de representar classes autenticáveis, mas se começássemos a estender esse sistema, logo encontraríamos algumas complicações. Em um banco de verdade, as divisões entre gerentes, diretores e clientes nem sempre são claras. Um Cliente , por exemplo, pode ser um Funcionario , um Funcionario pode ter outras subcategorias como fixos e temporários.
No Python, é possível que uma classe herde de várias outras classes. Poderíamos, por exemplo, criar uma classe A , que será superclasse das classes B e C . A herança múltipla não é muito difícil de entender se uma classe herda de várias classes que possuem propriedades completamente diferentes, mas as coisas ficam complicadas se duas superclasses implementam o mesmo método ou atributo.
Se as classes B e C herdarem a classe A e a classe D herdar as classes B e C , e as classes B e
C têm um método m2() , qual método a classe D herda?
class A:def m1(self):print('método de A')class B(A):def m2(self):print('método de B')class C(A):def m2(self):print('método de C')class D(B, C): passEssa ambiguidade é conhecida como o problema do diamante, ou problema do losango, e diferentes linguagens resolvem esse problema de maneiras diferentes. O Python segue uma ordem específica para percorrer a hierarquia de classes e essa ordem é chamada de MRO: Method Resolution Order (Ordem de Resolução de Métodos).
Toda classe tem um atributo mro que retorna uma tupla de referências das superclasses na
ordem MRO - da classe atual até a classe object . Vejamos o MRO da classe D :
print(D.mro())Saída:
(<class ’ main .D’>, <class ’ main .B’>, <class ’ main .C’>, <class ’ main .A’>, <class ‘obje ct’>)
A ordem é sempre da esquerda para direita. Repare que o Python vai procurar a chamada do método m2() primeiro na classe D , não encontrando vai procurar em B (a primeira classe herdada). Caso não encontre em B , vai procurar em C e só então procurar em A - e por último na classe object .
Também podemos acessar o atributo mro através do método mro() chamado pela classe que retorna uma lista ao invés de uma tupla:
print(D.mro())saída:
[<class ’ main .D’>, <class ’ main .B’>, <class ’ main .C’>, <class ’ main .A’>, <class ‘obje ct’>]
Portanto, seguindo o MRO, a classe D chama o método m2() da classe B :
d = D()
d.m1()
d.m2()
Saída:
método de A método de B
Felizmente, a função super() sabe como lidar de forma inteligente com herança múltipla. Se usála dentro do método, todos os métodos das superclasses devem ser chamados seguindo o MRO.
class A:def m1(self):print('método de A') class B(A):def m1(self):super().m1()
def m2(self):print('método de B')class C(A):def m1(self):super().m1()
def m2(self):print('método de C')class D(B, C): def m1(self):super().m1()
def m2(self):super().m2()
if name == ' main ': d = D()d.m1()
d.m2()
Gera a saída:
método de A método de B