typing.override: valide sobrescritas de métodos

Publicado em: 05/09/2026
Tempo de leitura: 5 minutos
Desenvolvedor revisando código Python e métodos sobrescritos

O decorator typing.override ajuda a deixar explícito que um método de uma subclasse substitui um método definido em uma classe base. Disponível na biblioteca padrão a partir do Python 3.12, ele não muda o comportamento do programa em tempo de execução: sua principal função é melhorar a análise estática, a revisão de código e a manutenção de hierarquias orientadas a objetos.

Em projetos pequenos, sobrescrever um método pode parecer trivial. Em bases grandes, porém, uma simples alteração no nome ou na assinatura do método da classe base pode fazer a subclasse deixar de sobrescrever o método esperado. O programa ainda pode executar, mas a regra de negócio muda silenciosamente. É exatamente esse tipo de problema que @override ajuda ferramentas como Pyright, mypy e IDEs modernas a detectar.

Exemplo básico

from typing import override

class Notificador:
    def enviar(self, mensagem: str) -> None:
        print(mensagem)

class NotificadorEmail(Notificador):
    @override
    def enviar(self, mensagem: str) -> None:
        print(f"E-mail: {mensagem}")

O decorator informa ao analisador: “este método deve existir em alguma classe ancestral”. Caso o nome seja digitado incorretamente, como envair, a ferramenta pode apontar que não existe nenhum método correspondente para sobrescrever. Sem essa declaração, o erro pode passar despercebido porque a subclasse apenas ganhou um método novo.

Por que usar typing.override

O primeiro benefício é a documentação executável. Quem lê a classe percebe imediatamente que o método pertence a um contrato herdado. Isso reduz a necessidade de navegar pela árvore de herança apenas para entender a intenção do código.

O segundo benefício é a segurança durante refatorações. Imagine que Notificador.enviar seja renomeado para notificar. As subclasses marcadas com @override passam a gerar alertas, mostrando exatamente quais implementações precisam ser atualizadas.

O terceiro benefício é a validação de assinatura. Analisadores estáticos podem comparar parâmetros, tipos e retorno com o método original. Mudanças incompatíveis ficam visíveis antes de chegarem à produção.

Compatibilidade de assinaturas

Uma sobrescrita deve respeitar o princípio de substituição: uma instância da subclasse deve poder ser usada onde a classe base é esperada. Por isso, tornar parâmetros mais restritivos normalmente é problemático.

from typing import override

class Repositorio:
    def salvar(self, dado: object) -> bool:
        return True

class RepositorioTexto(Repositorio):
    @override
    def salvar(self, dado: str) -> bool:
        return bool(dado)

Nesse exemplo, a classe base aceita qualquer object, enquanto a subclasse aceita apenas str. Um analisador pode rejeitar a implementação porque código que conhece apenas Repositorio poderia chamar salvar(123). A subclasse quebraria o contrato.

Já o tipo de retorno pode frequentemente ser mais específico. Se a classe base retorna Animal, uma subclasse pode retornar Cachorro, desde que Cachorro seja subtipo de Animal.

Métodos abstratos

@override funciona muito bem com abc.ABC e @abstractmethod. O método abstrato define a obrigação; @override mostra que a implementação concreta atende a essa obrigação.

from abc import ABC, abstractmethod
from typing import override

class Conversor(ABC):
    @abstractmethod
    def converter(self, valor: str) -> int:
        raise NotImplementedError

class ConversorDecimal(Conversor):
    @override
    def converter(self, valor: str) -> int:
        return int(valor, 10)

Essa combinação deixa a arquitetura clara: a classe base declara o contrato e a classe concreta declara sua intenção de implementá-lo.

Properties, classmethods e staticmethods

O decorator também pode ser usado ao substituir propriedades e outros descritores. A ordem dos decorators merece atenção. Em geral, coloque @override abaixo de @classmethod, @staticmethod ou @property, diretamente sobre a função que está sendo marcada.

from typing import override

class Documento:
    @property
    def formato(self) -> str:
        return "genérico"

class PDF(Documento):
    @property
    @override
    def formato(self) -> str:
        return "pdf"

Ferramentas podem variar em detalhes de suporte, por isso vale manter o analisador e a IDE atualizados.

Herança múltipla

Em herança múltipla, @override não informa qual ancestral está sendo substituído. Ele apenas afirma que existe um membro compatível na resolução de métodos. Para entender qual implementação será chamada com super(), ainda é necessário conhecer a MRO, a ordem de resolução de métodos.

class Logavel:
    def executar(self) -> None:
        print("log")

class Auditavel:
    def executar(self) -> None:
        print("auditoria")

class Servico(Logavel, Auditavel):
    @override
    def executar(self) -> None:
        super().executar()
        print("serviço")

A chamada de super() segue a MRO de Servico. O decorator não altera essa ordem.

Protocolos e interfaces estruturais

Um ponto importante é que @override foi pensado para relações de herança. Implementar informalmente a mesma assinatura de um Protocol sem herdar dele pode não ser considerado uma sobrescrita. Protocolos trabalham com tipagem estrutural: o objeto é compatível porque possui os membros esperados, não necessariamente porque deriva da interface.

Quando a intenção é apenas provar compatibilidade com um protocolo, prefira anotações de tipo e testes estáticos. Use @override quando houver uma classe ancestral real com o membro correspondente.

Compatibilidade com versões antigas

Para projetos que ainda suportam Python 3.11 ou anterior, use typing_extensions.override. O pacote typing_extensions disponibiliza recursos novos de tipagem para versões anteriores.

try:
    from typing import override
except ImportError:
    from typing_extensions import override

Em bibliotecas, também é possível declarar a dependência mínima de typing_extensions e importar sempre do mesmo lugar.

O decorator não substitui testes

@override detecta inconsistências estruturais, mas não valida a lógica do método. Uma implementação pode ter assinatura perfeita e ainda calcular o resultado errado. Portanto, combine análise estática, testes unitários e testes de integração.

Também é útil configurar o analisador para exigir @override em toda sobrescrita. Algumas ferramentas oferecem uma regra que alerta quando um método substitui outro sem o decorator. Isso torna o padrão consistente no projeto.

Boas práticas

Adote @override principalmente em frameworks internos, plugins, adaptadores, repositórios, serviços e classes com várias subclasses. Evite hierarquias profundas apenas porque o decorator existe; composição ainda costuma ser uma solução mais simples.

Mantenha assinaturas compatíveis, preserve invariantes da classe base e documente diferenças comportamentais. Quando chamar super(), saiba se a implementação ancestral precisa ser executada antes ou depois da lógica especializada.

Para aprofundar temas relacionados, consulte nossos guias sobre introspecção com inspect, dataclass_transform, TypeVarTuple e StrEnum. A documentação oficial de typing.override e a PEP 698 são as referências externas principais.

Conclusão

typing.override é um recurso pequeno, mas valioso. Ele transforma uma intenção implícita em uma declaração verificável, melhora revisões, reduz falhas de refatoração e documenta contratos de herança. Em projetos orientados a objetos, usar o decorator de forma consistente torna a evolução das classes mais segura sem adicionar custo relevante em tempo de execução.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Desenvolvedor trabalhando com filas e threads em Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue.SimpleQueue: fila FIFO segura entre threads

    Aprenda queue.SimpleQueue no Python para criar filas FIFO seguras entre threads, organizar workers e evitar erros de concorrência.

    Ler mais

    Tempo de leitura: 5 minutos
    04/09/2026
    Desenvolvedor trabalhando com enums e código Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    StrEnum no Python: enums como strings

    Aprenda StrEnum no Python para criar enums como strings, validar entradas, serializar JSON e organizar APIs e configurações.

    Ler mais

    Tempo de leitura: 4 minutos
    04/09/2026
    Pastas e diretórios para contextlib.chdir no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextlib.chdir: restaure o diretório automaticamente

    Aprenda contextlib.chdir no Python para trocar diretórios temporariamente com segurança, testes confiáveis e restauração automática do caminho.

    Ler mais

    Tempo de leitura: 5 minutos
    03/09/2026
    Monitoramento de desempenho e execução de código Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sys.monitoring: instrumentação de baixo overhead

    Aprenda sys.monitoring no Python para instrumentar execução com baixo overhead, eventos, callbacks, ferramentas e observabilidade segura.

    Ler mais

    Tempo de leitura: 6 minutos
    03/09/2026
    Desenvolvedor organizando dados com operator.attrgetter no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    operator.attrgetter: ordene objetos por atributos

    Aprenda operator.attrgetter no Python para ordenar, agrupar e transformar objetos por atributos simples ou aninhados com código mais claro.

    Ler mais

    Tempo de leitura: 5 minutos
    02/09/2026
    Programação assíncrona com asyncio.Runner no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.Runner: reutilize o event loop com segurança

    Aprenda asyncio.Runner no Python para reutilizar o event loop, controlar contexto, sinais, debug e encerramento assíncrono com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    02/09/2026