Capítulo 09: Herança, Polimorfismo, Classes Abstratas (abc.ABC) e MRO
Especialização em Sistemas com Python & POO com Type Hints • Capítulo 09
🗺️ Mapa Conceitual do Tópico
flowchart TD
subgraph INHERITANCE["Herança e Polimorfismo Nominal"]
LSP["Princípio de Substituição de Liskov (SOLID LSP)\nSubclasses Devem Honrar os Contratos da Superclasse"]
ABC_MOD["Módulo abc (Abstract Base Classes)\nMetaclasse ABCMeta · @abstractmethod · @abstractproperty"]
end
subgraph MULTIPLE["Herança Múltipla e Resolução Cooperativa"]
DIAMOND["Problema do Diamante (Ambiguidade Estrutural)"]
C3["Algoritmo C3 Linearization\nOrdem Determinística & Consistência Monotônica"]
MRO["Method Resolution Order (Classe.mro())\nCadeia de Invocação Cooperativa via super()"]
end
subgraph PATTERNS["Padrões Arquiteturais OO"]
TEMPLATE["Template Method Pattern\nEsqueleto Abstrato com Hooks Concretos"]
MIXIN["Classes Mixin Ortogonais\nExtensão de Capacidades Sem Poluir a Hierarquia"]
end
INHERITANCE --> MULTIPLE
MULTIPLE --> MRO
ABC_MOD --> TEMPLATE
MULTIPLE --> MIXIN
style INHERITANCE fill:#ede7f6,stroke:#512da8,stroke-width:2px
style MULTIPLE fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style PATTERNS fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
🧬 1. Classes Abstratas Formais (abc.ABC) e Contratos Rígidos
Em Python, a herança nominal pura permite compartilhar código e estabelecer uma relação semântica do tipo “É-Um” (IS-A). Para prevenir que classes base incompletas sejam instanciadas no sistema, a biblioteca padrão provê o módulo abc (Abstract Base Classes):
- Metaclasse
ABCMeta(ou herança direta deabc.ABC): Intercepta o método__new__durante a instanciação. Se a classe ou subclasse contiver qualquer método decorado com@abstractmethodnão implementado, o interpretador aborta comTypeError: Can't instantiate abstract class ... with abstract method. - Métodos Abstratos com Corpo: Diferente de interfaces em C++ ou Java clássico, métodos abstratos em Python podem conter código compartilhado. Subclasses podem sobrescrever o método e chamar a lógica comum através de
super().metodo(). - Propriedades Abstratas: Combinam
@propertye@abstractmethodpara exigir que a subclasse implemente getters ou setters específicos.
from abc import ABC, abstractmethod
from decimal import Decimal
class ProcessadorPagamento(ABC):
@property
@abstractmethod
def taxa_administrativa(self) -> Decimal:
"""Propriedade abstrata obrigatória em todas as subclasses."""
...
@abstractmethod
def liquidar(self, valor: Decimal) -> bool:
"""Método abstrato de liquidação."""
...
💎 2. O Problema do Diamante e o Algoritmo C3 Linearization (MRO)
Em linguagens com suporte a Herança Múltipla, surge o clássico dilema do diamante: se a classe D herda de B e C, e ambas herdam de A, qual versão de um método sobrescrito D deve executar?
classDiagram
direction TB
class A {
+executar()
}
class B {
+executar()
}
class C {
+executar()
}
class D {
+executar()
}
A <|-- B
A <|-- C
B <|-- D
C <|-- D
O Algoritmo C3 Linearization
Desde o Python 2.3 e mantido em todas as versões do Python 3, a ordem de busca é resolvida deterministicamente pelo algoritmo C3 Linearization, garantindo três propriedades fundamentais:
- Preservação da Precedência Local: Se na declaração consta
class D(B, C):, os métodos deBsempre serão avaliados antes dos deC. - Monotonicidade: Se uma classe precede outra na hierarquia de uma superclasse, essa ordem relativa é mantida em todas as subclasses derivadas.
- Ausência de Ciclos: O compilador rejeita hierarquias inconsistentes em tempo de definição.
A Função super() e Chamadas Cooperativas
Em Python, super() NÃO chama o pai direto, mas sim o próximo elemento na lista linearizada do MRO:
class D(B, C):
def executar(self) -> None:
super().executar() # Consulta D.mro() para saber quem chamar!
# Inspecionando o MRO no terminal:
print([cls.__name__ for cls in D.mro()])
# Saída: ['D', 'B', 'C', 'A', 'object']
🧩 3. Padrão Mixin: Reuso Transversal sem Acoplamento
Um Mixin é uma classe projetada para fornecer funcionalidades pontuais e reutilizáveis a outras classes via herança múltipla, respeitando três regras canônicas de design:
- Não deve ser instanciada diretamente.
- Não deve possuir estado de instância próprio (não define
__init__próprio ou utilizasuper().__init__). - Não herda de classes de domínio; atua como um modificador de capacidade.
import json
from datetime import datetime, timezone
class AuditavelMixin:
"""Mixin que confere capacidade de auditoria e carimbo temporal a qualquer entidade."""
def obter_metadados_auditoria(self) -> dict[str, str]:
return {
"classe": self.__class__.__name__,
"id_memoria": hex(id(self)),
"timestamp_utc": datetime.now(timezone.utc).isoformat()
}
class SerializavelJSONMixin:
"""Mixin para exportação rápida de dicionários em formato JSON."""
def to_json(self) -> str:
return json.dumps(getattr(self, "__dict__", {}), default=str)
🏛️ 4. Template Method Pattern
O padrão Template Method da GoF define o esqueleto de um algoritmo na superclasse abstrata, delegando passos específicos para as subclasses concretas:
sequenceDiagram
participant Cliente
participant Pipeline as PipelineExportacao (Superclasse)
participant CSV as ExportadorCSV (Subclasse)
Cliente->>Pipeline: executar_exportacao()
Pipeline->>Pipeline: autenticar_requisicao()
Pipeline->>CSV: extrair_registros()
Pipeline->>CSV: formatar_linhas()
Pipeline->>Pipeline: registrar_log_auditoria()
Pipeline-->>Cliente: Arquivo Final Gerado
Subclasses customizam a extração e a formatação sem quebrar o fluxo coordenado de segurança e auditoria da superclasse.