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

    A vibrant collection of blue sewing threads arranged with hands on a white background.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: coordene threads

    Aprenda queue no Python para coordenar threads com FIFO, prioridade, backpressure, task_done, join, retries e shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    A male software engineer working on code in a modern office setting.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    struct no Python: trabalhe com binário

    Aprenda struct no Python para empacotar dados binários, controlar endianness, usar buffers e validar protocolos e arquivos externos.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile no Python: crie TAR seguro

    Aprenda tarfile no Python para criar TAR comprimido, inspecionar membros e extrair com filtros, limites e proteção contra path traversal.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Row of colorful office binders neatly arranged on a shelf, ideal for organization concepts.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    gzip no Python: comprima arquivos .gz

    Aprenda gzip no Python para ler e gravar .gz, criar saídas reproduzíveis, trabalhar com streams e limitar a expansão de

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026
    Color-coded office binders organized neatly in a storage shelf, featuring labels and a striking red binder.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    lzma no Python: comprima arquivos XZ

    Aprenda lzma no Python para criar arquivos XZ, usar streams, checks, filtros e limites de memória ao descompactar dados externos.

    Ler mais

    Tempo de leitura: 5 minutos
    17/08/2026
    Exquisite python skin handbag with intricate snake emblem and elegant design.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    bz2 no Python: comprima com bzip2

    Aprenda bz2 no Python para comprimir arquivos e bytes, processar fluxos em blocos e limitar a expansão de dados externos.

    Ler mais

    Tempo de leitura: 5 minutos
    16/08/2026