abc no Python: classes abstratas

Publicado em: 06/08/2026
Tempo de leitura: 6 minutos
Arquitetura de software representando classes abstratas com abc no Python

Projetos orientados a objetos frequentemente precisam definir um contrato comum para várias implementações. Uma classe de pagamento deve expor processar(); um repositório precisa oferecer salvar() e buscar(); um plugin deve declarar métodos obrigatórios. O módulo abc no Python fornece classes base abstratas, métodos obrigatórios e suporte a subclasses virtuais para representar esses contratos no runtime.

Neste guia, você aprenderá a usar ABC, ABCMeta, abstractmethod, register(), __subclasshook__() e update_abstractmethods(). O conteúdo complementa nossos artigos sobre types no Python, inspect, singledispatch, descriptors e dataclasses.

O que é uma classe abstrata

Uma classe abstrata descreve comportamento esperado e pode fornecer implementação parcial, mas não deve ser instanciada enquanto possuir métodos abstratos não resolvidos.

from abc import ABC, abstractmethod

class Armazenamento(ABC):
    @abstractmethod
    def salvar(self, chave, valor):
        pass

    @abstractmethod
    def buscar(self, chave):
        pass

Tentar criar Armazenamento() gera TypeError. Uma subclasse concreta precisa implementar todos os membros abstratos.

Criar uma implementação concreta

class ArmazenamentoMemoria(Armazenamento):
    def __init__(self):
        self._dados = {}

    def salvar(self, chave, valor):
        self._dados[chave] = valor

    def buscar(self, chave):
        return self._dados[chave]

repositorio = ArmazenamentoMemoria()
repositorio.salvar("usuario", "Ana")
print(repositorio.buscar("usuario"))

Além de comunicar o contrato, a classe base impede a criação acidental de implementações incompletas.

ABC é uma classe auxiliar

ABC usa ABCMeta como metaclass. Herdar de ABC é o modo mais simples de declarar uma classe base abstrata.

Quando a classe já precisa de outra metaclass, pode existir conflito. Nesse caso, derive uma metaclass compatível de ABCMeta ou use composição em vez de misturar hierarquias complexas.

abstractmethod

@abstractmethod marca um método como obrigatório. Ele deve ser o decorator mais interno quando combinado com classmethod, staticmethod ou property.

class Fabrica(ABC):
    @classmethod
    @abstractmethod
    def criar(cls, configuracao):
        pass

    @staticmethod
    @abstractmethod
    def validar(configuracao):
        pass

A ordem importa porque abstractmethod precisa marcar o descriptor subjacente.

Propriedades abstratas

class Documento(ABC):
    @property
    @abstractmethod
    def titulo(self):
        pass

Uma subclasse pode implementar a propriedade com outro @property.

class Relatorio(Documento):
    def __init__(self, titulo):
        self._titulo = titulo

    @property
    def titulo(self):
        return self._titulo

Decorators antigos como abstractproperty, abstractclassmethod e abstractstaticmethod existem por compatibilidade, mas as combinações modernas são preferíveis.

Métodos abstratos podem ter implementação

Um método abstrato não precisa conter apenas pass. Ele pode fornecer comportamento comum que subclasses chamam com super().

class Exportador(ABC):
    @abstractmethod
    def exportar(self, dados):
        self._validar(dados)

    def _validar(self, dados):
        if not dados:
            raise ValueError("dados vazios")

class ExportadorJSON(Exportador):
    def exportar(self, dados):
        super().exportar(dados)
        return json.dumps(dados)

Esse padrão cria um ponto final cooperativo para herança múltipla e evita duplicar validações.

Implementação parcial

Uma subclasse pode continuar abstrata.

class ArmazenamentoComLog(Armazenamento):
    def salvar(self, chave, valor):
        print("salvando", chave)
        return self._salvar_impl(chave, valor)

    @abstractmethod
    def _salvar_impl(self, chave, valor):
        pass

Ela implementa parte do contrato e introduz uma nova obrigação para subclasses concretas.

Verificar abstração

A metaclass mantém o conjunto __abstractmethods__.

print(Armazenamento.__abstractmethods__)
print(ArmazenamentoMemoria.__abstractmethods__)

Frameworks podem usar inspect.isabstract() para uma API mais clara.

ABCMeta diretamente

Também é possível declarar a metaclass explicitamente.

from abc import ABCMeta

class Servico(metaclass=ABCMeta):
    @abstractmethod
    def executar(self):
        pass

Esse estilo é útil quando você precisa compreender ou combinar metaclasses, mas herdar de ABC costuma ser mais legível.

Subclasses virtuais

register() permite considerar uma classe existente como subclasse sem alterar sua herança.

class Leitor(ABC):
    @abstractmethod
    def ler(self):
        pass

class LeitorLegado:
    def ler(self):
        return "dados"

Leitor.register(LeitorLegado)

print(issubclass(LeitorLegado, Leitor))
print(isinstance(LeitorLegado(), Leitor))

O registro não adiciona métodos, não chama código da ABC e não força a implementação do contrato. Ele apenas altera as respostas de issubclass() e isinstance().

Risco das subclasses virtuais

Registrar uma classe incorreta produz um falso senso de compatibilidade.

class Incompleto:
    pass

Leitor.register(Incompleto)
print(isinstance(Incompleto(), Leitor))  # True

Use registro somente quando o tipo já implementa semanticamente o contrato. Testes de conformidade continuam necessários.

register() como decorator

O método devolve a classe registrada e pode ser usado como decorator.

@Leitor.register
class OutroLeitor:
    def ler(self):
        return "outro"

Esse formato deixa a intenção próxima da implementação, mas também cria dependência direta da ABC.

__subclasshook__()

Uma ABC pode reconhecer subclasses estruturalmente sem registro explícito.

class IteravelPersonalizado(ABC):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is IteravelPersonalizado:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

Agora classes que definem __iter__ diretamente na hierarquia podem ser reconhecidas por issubclass().

Retornar True, False ou NotImplemented

__subclasshook__() pode retornar:

  • True: considerar subclasse;
  • False: rejeitar mesmo se outras regras sugerirem o contrário;
  • NotImplemented: continuar o algoritmo normal.

Prefira NotImplemented quando o hook não consegue decidir. Retornar False é uma decisão forte.

Hooks simples e conservadores

Verificar apenas a presença de um nome não garante assinatura, semântica ou tipos corretos. Um método ler pode fazer algo completamente diferente.

Use hooks para protocolos bem conhecidos e fáceis de reconhecer. Para contratos ricos, herança explícita, Protocol e testes são mais seguros.

ABC versus Protocol

Classes base abstratas trabalham principalmente com herança nominal e verificações de runtime. typing.Protocol descreve tipagem estrutural para ferramentas estáticas.

from typing import Protocol

class PodeLer(Protocol):
    def ler(self) -> str: ...

Uma classe com método compatível satisfaz o protocolo para o type checker sem herdar dele. Protocols com @runtime_checkable permitem verificações limitadas em runtime, mas não validam assinaturas profundamente.

ABC versus duck typing

Python permite simplesmente chamar objeto.ler() e tratar AttributeError quando não existe. Essa abordagem é flexível e reduz acoplamento.

Use uma ABC quando há valor em documentação central, implementação compartilhada, construção impedida para classes incompletas ou registro com isinstance().

Herança múltipla

Múltiplas ABCs podem compor contratos.

class Legivel(ABC):
    @abstractmethod
    def ler(self): ...

class Gravavel(ABC):
    @abstractmethod
    def gravar(self, dados): ...

class ArquivoVirtual(Legivel, Gravavel):
    def ler(self):
        return self.dados

    def gravar(self, dados):
        self.dados = dados

Mantenha métodos cooperativos com super() e evite estado conflitante nas bases.

Atualizar métodos abstratos dinamicamente

Adicionar uma implementação após a criação da classe não atualiza automaticamente o conjunto de abstratos. Use update_abstractmethods().

from abc import update_abstractmethods

class Dinamica(ABC):
    @abstractmethod
    def executar(self):
        pass

Dinamica.executar = lambda self: "ok"
update_abstractmethods(Dinamica)

print(Dinamica().executar())

A documentação oficial de abc recomenda essa função para alterações dinâmicas. Ela assume que as superclasses já foram atualizadas.

Cache das verificações

ABCMeta mantém caches internos para acelerar issubclass(). get_cache_token() devolve um token que muda quando o registro de subclasses virtuais é alterado.

from abc import get_cache_token

token_antes = get_cache_token()
Leitor.register(NovoLeitor)
token_depois = get_cache_token()
assert token_antes != token_depois

Frameworks que mantêm caches próprios de conformidade podem invalidá-los quando o token muda.

Plugins

Uma ABC pode ser a base de plugins concretos.

class Plugin(ABC):
    nome: str

    @abstractmethod
    def iniciar(self, contexto):
        pass

    @abstractmethod
    def encerrar(self):
        pass

Ao carregar um plugin, verifique se a classe é concreta, valide metadados e trate falhas de inicialização. Herança não fornece isolamento ou segurança.

Dependência e testes

Receber uma ABC no construtor torna o contrato explícito.

class Aplicacao:
    def __init__(self, armazenamento: Armazenamento):
        self.armazenamento = armazenamento

Em testes, implemente um fake pequeno que satisfaça o contrato. Evite mocks excessivamente permissivos que aceitariam métodos inexistentes.

Alterações de API

Adicionar um novo método abstrato quebra todas as subclasses concretas existentes. É uma mudança incompatível.

Antes de tornar um método obrigatório, considere fornecer implementação padrão, criar uma ABC nova ou introduzir uma capability separada.

Erros frequentes

  • Colocar @abstractmethod fora de @classmethod ou @property.
  • Presumir que uma subclasse virtual recebeu métodos.
  • Registrar classes que não cumprem o contrato.
  • Usar hooks estruturais amplos demais.
  • Adicionar métodos abstratos sem planejar compatibilidade.
  • Criar hierarquias profundas para compartilhar pouco código.
  • Confundir ABC com sandbox de plugins.
  • Modificar a classe sem update_abstractmethods().

Boas práticas

  • Mantenha contratos pequenos e coesos.
  • Forneça implementação comum quando útil.
  • Use super() em herança cooperativa.
  • Registre subclasses virtuais somente após testes.
  • Prefira NotImplemented em hooks incertos.
  • Compare ABCs com Protocols e duck typing.
  • Trate novos abstratos como mudança incompatível.
  • Teste subclasses concretas com uma suíte compartilhada.

Conclusão

O módulo abc no Python permite definir contratos de runtime, impedir instanciação de classes incompletas e compartilhar implementação entre subclasses. ABC e abstractmethod cobrem a maioria dos casos, enquanto registro virtual e __subclasshook__() oferecem integração estrutural.

Classes abstratas funcionam melhor quando o contrato é pequeno, estável e semanticamente claro. Com hooks conservadores, testes de conformidade e cuidado ao evoluir a API, ABCs tornam extensões e dependências explícitas sem transformar o projeto em uma hierarquia rígida.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código e estruturas representando tipos do runtime com o módulo types no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    types no Python: tipos do runtime

    Aprenda types no Python para usar SimpleNamespace, MappingProxyType, tipos do runtime e criação dinâmica de classes.

    Ler mais

    Tempo de leitura: 6 minutos
    06/08/2026
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sched no Python: agende eventos

    Aprenda sched no Python para agendar eventos, controlar prioridades, cancelar tarefas e criar repetições com relógio monotônico.

    Ler mais

    Tempo de leitura: 8 minutos
    05/08/2026
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    atexit no Python: execute limpeza ao sair

    Aprenda atexit no Python para executar limpeza no encerramento, controlar ordem LIFO e evitar problemas com threads, sinais e exceções.

    Ler mais

    Tempo de leitura: 7 minutos
    05/08/2026
    Monitor com código binário representando análise de opcodes pickle com pickletools no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickletools no Python: analise pickles

    Aprenda pickletools no Python para desmontar pickles, analisar opcodes e otimizar fluxos sem executar dados não confiáveis.

    Ler mais

    Tempo de leitura: 7 minutos
    05/08/2026
    Código-fonte em tela representando análise com tokenize no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tokenize no Python: analise código-fonte

    Aprenda tokenize no Python para ler tokens, comentários, indentação, codificação, posições e reconstruir código-fonte com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    05/08/2026
    Desenvolvedor trabalhando em automação de build e compilação de diretórios com compileall no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compileall no Python: compile diretórios

    Aprenda compileall no Python para compilar diretórios, gerar pyc em paralelo, filtrar arquivos e controlar otimização e invalidação.

    Ler mais

    Tempo de leitura: 7 minutos
    04/08/2026