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): pass

Essa 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


⬅️ Capítulo Anterior | Próximo Capítulo ➡️