abc: crie classes abstratas e contratos

Atualizado em: 20/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

    Programador analisando código para identificar tipos MIME de arquivos no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecte tipos MIME

    Aprenda mimetypes.guess_file_type no Python para detectar tipos MIME em caminhos, URLs, uploads e respostas HTTP com fallbacks seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Componentes de servidor representando interpretadores Python executando em paralelo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real no Python

    Aprenda InterpreterPoolExecutor no Python para executar tarefas CPU-bound em interpretadores isolados com paralelismo real.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessador representando CPUs disponíveis para um processo Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: conte CPUs disponíveis

    Aprenda os.process_cpu_count no Python para dimensionar workers conforme as CPUs realmente disponíveis ao processo.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Laptop com código digital representando dados BLOB no SQLite
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: leia BLOBs sem carregar tudo na memória

    Aprenda sqlite3.Blob no Python para ler e gravar BLOBs em partes, reduzir memória e trabalhar com dados binários no SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Análise estatística para random.binomialvariate no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: simule distribuições binomiais

    Aprenda random.binomialvariate no Python para simular sucessos, validar probabilidades e analisar cenários binomiais com clareza.

    Ler mais

    Tempo de leitura: 6 minutos
    11/09/2026
    Código Python analisado com inspect.signature.bind
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature.bind: valide argumentos de funções

    Aprenda inspect.signature.bind no Python para validar argumentos, aplicar padrões e criar decorators e APIs dinâmicas com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    11/09/2026