O Modelo Mental de Verificação de Tipos
Dicas de tipo são anotações que você anexa a variáveis, parâmetros e valores de retorno.
Busque em todas as páginas da documentação
Dicas de tipo são anotações que você anexa a variáveis, parâmetros e valores de retorno.
Elas descrevem a forma dos dados que um pedaço de código espera e produz.
O próprio interpretador faz quase nada com elas em tempo de execução.
O trabalho real acontece em um programa separado, um verificador de tipo estático como mypy ou pyright, que lê seu código-fonte, constrói um modelo do tipo de cada valor e relata onde esse modelo falha.
Noções Básicas de Dicas de Tipo mostra a sintaxe; Protocolos e Tipagem Estrutural, Genéricos e TypeVar e Estratégia de Tipagem Gradual cobrem cada faceta em profundidade.
Esta página é a camada abaixo de todas elas: o que um verificador realmente está fazendo e por que isso é um tipo fundamentalmente diferente de segurança do que o interpretador fornece.
None não tratado, um atributo com erro de digitação - antes que o código seja executado, sem abrir mão do modelo de objeto dinâmico do Python.Python lançou a sintaxe de anotação (x: int, def f() -> str) anos antes de ter um significado oficial.
PEP 484 deu a essa sintaxe um propósito: um vocabulário padrão que ferramentas externas poderiam ler para raciocinar sobre os tipos de um programa sem executá-lo.
Crucialmente, o comportamento do interpretador não mudou.
def add(a: int, b: int) -> int: return a + b é executado exatamente da mesma forma, quer você chame add(1, 2) ou add("x", "y") - as anotações são armazenadas em __annotations__ e, de outra forma, inertes em tempo de execução.
Uma analogia útil é uma planta de construção revisada por um inspetor antes do início da construção.
A aprovação do inspetor (uma execução limpa do mypy) informa que o projeto é sólido no papel.
Isso não impede fisicamente uma equipe de construção de construir algo diferente da planta - o runtime do Python é essa equipe, e ela nunca consulta a planta enquanto trabalha.
É por isso que a abordagem do Python é chamada de tipagem gradual: uma base de código pode misturar módulos totalmente anotados, módulos parcialmente anotados e código legado completamente não anotado, e o verificador raciocina sobre cada parte com as informações que possui, recorrendo a um Any implícito onde quer que as dicas estejam faltando.
Essa única escolha de design é o que torna a adição de tipos a uma base de código grande e existente tratável, em vez de uma reescrita completa.
O verdadeiro trabalho de um verificador de tipo é a inferência estática: percorrer seu código-fonte sem executá-lo, atribuir um tipo a cada expressão e sinalizar locais onde uma operação é inconsistente com os tipos que ele inferiu.
Duas maneiras diferentes de decidir "este valor satisfaz esse tipo" são importantes aqui, e misturá-las é a fonte mais comum de confusão nesta seção.
A tipagem nominal pergunta sobre ancestralidade - um valor satisfaz um tipo se sua classe for esse tipo ou uma subclasse dele, verificado através da cadeia de herança explícita.
A tipagem estrutural pergunta sobre a forma em vez disso - um valor satisfaz um tipo se ele tiver os métodos e atributos corretos, independentemente de qual classe ele realmente é ou do que ele herda.
A hierarquia de classes embutida do Python é nominal por padrão, mas Protocol (PEP 544) adiciona tipagem estrutural por cima, que é por que um Protocol nunca exige que as classes que o satisfazem herdem de algo.
Runtime (o que realmente executa) Camada estática (o que mypy/pyright vê)
----------------------------------- --------------------------------------
def greet(name: str) -> str: greet: (str) -> str
return f"Hi {name}" name inferido como str dentro do corpo
greet(42) # executa, imprime "Hi 42" greet(42) # MARCADO: int não é str
A lacuna nesse diagrama é o ponto principal: o runtime nunca impede greet(42), mas o modelo do programa do verificador o faz, porque ele raciocina puramente a partir de tipos declarados e inferidos, nunca a partir do que uma determinada execução realmente faz.
Genéricos estendem essa mesma maquinaria de inferência para formas paramétricas - um list[int] e um list[str] são tipos diferentes para o verificador, mesmo que ambos sejam, em tempo de execução, exatamente a mesma classe list.
TypeVar e a sintaxe type do PEP 695 permitem que você escreva uma função ou classe cujos tipos estão vinculados entre os parâmetros (um TypeVar vinculado ao tipo do elemento de um contêiner, por exemplo), para que o verificador possa verificar que o que quer que saia de uma função genérica é consistente com o que entrou, sem que você escreva essa função nove vezes para nove tipos concretos.
Como a inferência é tão boa quanto as informações disponíveis, os verificadores expõem dials de rigor em vez de um único interruptor on/off - disallow_untyped_defs, warn_return_any, modo strict, substituições por módulo em pyproject.toml.
Uma base de código grande quase nunca adota o modo estrito completo em todos os lugares de uma vez; em vez disso, ela aperta dial por dial, módulo por módulo, que é todo o assunto de Estratégia de Tipagem Gradual.
Any é o escape hatch designado do verificador, e ele merece respeito especial porque é contagioso: uma vez que um valor é tipado como Any, toda expressão construída a partir dele também é implicitamente Any, desativando silenciosamente a verificação para tudo a jusante até que uma fronteira o reanote.
Verificação de tipo estática e validação em tempo de execução também são fáceis de confundir, mas resolvem problemas diferentes.
Um verificador como mypy prova que seu código é internamente consistente, dados os tipos que você declarou; ele não pode verificar se os dados que chegam via HTTP ou de um arquivo realmente correspondem a esses tipos, porque esses dados não existem quando o verificador é executado.
Essa é precisamente a lacuna que bibliotecas como Pydantic preenchem - validação em tempo de execução na fronteira, tipos estáticos em todos os lugares atrás dela - e por que TypedDict documenta uma forma JSON para benefício do verificador sem validar nada quando o programa realmente é executado.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Tipagem nominal (hierarquias de classe, ABCs) | Intenção explícita, amigável para IDE, funciona bem para árvores de classe próprias | Requer herança; estranho para código de terceiros ou objetos ad-hoc | Seu próprio modelo de domínio com uma relação real de é-um |
Tipagem estrutural (Protocol) | Nenhuma herança necessária; se encaixa bem em código duck-typed e de terceiros | Fácil de subespecificar uma forma; menos descobrível do que uma classe base concreta | Portas/interfaces, pontos de teste, adaptação de tipos externos |
| Validação em tempo de execução (Pydantic) | Realmente impõe a forma quando o programa é executado, com mensagens de erro reais | Custo de tempo de execução; uma segunda definição de esquema para manter em sincronia com os tipos estáticos | Dados cruzando uma fronteira não confiável - HTTP, arquivos, filas |
Sem dicas (Any / não tipado) | Zero atrito, mais rápido para prototipar | Nenhuma garantia estática; bugs surgem como tracebacks de produção | Scripts descartáveis, notebooks exploratórios |
A tipagem estática em Python também continuou evoluindo em vez de ficar parada: a sintaxe de união X | Y do PEP 604, a sintaxe genérica mais concisa do PEP 695, e cada lançamento de mypy/pyright que refina mais padrões match/isinstance fazem parte do fechamento da lacuna entre o que o sistema de tipos pode expressar e o que o Python idiomático já parece.
Any é uma maneira segura e neutra de silenciar um erro do verificador." Any desativa a verificação para esse valor e tudo o que é derivado dele, então um único Any em uma fronteira pode apagar silenciosamente a segurança de tipo através de toda uma cadeia de chamadas.Protocol é apenas uma classe base abstrata com um decorador extra." Um Protocol corresponde por forma, nunca por herança - uma classe o satisfaz apenas por ter os métodos corretos, sem a necessidade de class Foo(MyProtocol) em qualquer lugar.Não. O interpretador armazena anotações em __annotations__ e, de outra forma, as ignora; o comportamento em tempo de execução é idêntico com ou sem dicas.
Um verificador de tipo estático separado - mypy ou pyright são os dois dominantes - lê seu código-fonte sem executá-lo e relata inconsistências com os tipos que você declarou ou que ele inferiu.
Significa que código tipado e não tipado podem coexistir na mesma base de código. Código não anotado é tratado como implicitamente Any, então um verificador ainda pode analisar as partes que têm dicas sem exigir que todo o programa seja tipado de uma vez.
A tipagem nominal verifica a ancestralidade da classe - um valor corresponde a um tipo se sua classe for esse tipo ou uma subclasse. A tipagem estrutural (via Protocol) verifica a forma em vez disso - um valor corresponde se ele tiver os métodos e atributos corretos, independentemente da herança.
Porque é contagioso. Toda expressão derivada de um valor Any também é implicitamente Any, então a verificação é silenciosamente desativada para toda uma cadeia a jusante a partir de um único valor não anotado ou explicitamente Any.
Não diretamente. Um verificador raciocina a partir de tipos declarados e inferidos, não de valores reais em tempo de execução, então ele não pode capturar um bug causado por, digamos, JSON malformado de uma API - isso requer validação em tempo de execução, não análise estática.
Para valores internos, já tipados, dicas mais um verificador geralmente são suficientes. Para dados que entram no programa de fora - corpos HTTP, arquivos de configuração, mensagens de fila - você precisa de algo que valide em tempo de execução, já que o verificador nunca vê esses dados antes de serem executados.
Em tempo de execução não há diferença - ambos são a mesma classe list. Estaticamente, no entanto, list[int] informa ao verificador o que está dentro, para que ele possa capturar código que tenta anexar uma string ou iterar assumindo o tipo de elemento errado.
Não. Configurações de rigor como disallow_untyped_defs ou warn_return_any podem ser habilitadas por módulo em pyproject.toml, que é como a maioria das bases de código migra - apertando um pacote de cada vez em vez de virar um único interruptor global.
Porque o código real precisa de ambas. Seu próprio modelo de domínio frequentemente tem relacionamentos de herança genuínos que valem a pena expressar nominalmente, enquanto objetos de terceiros ou duck-typed precisam ser descritíveis apenas por forma - Protocol existe especificamente para cobrir esse segundo caso sem forçar a herança em código que você não possui.
Não - ele apenas prova que o código é internamente consistente com os tipos que você declarou ou que o verificador inferiu. Erros de lógica, incompatibilidades de dados em tempo de execução e qualquer coisa fora do vocabulário do sistema de tipos ainda podem passar.
É mais curto e lê mais próximo de como as pessoas já descrevem tipos em prosa, e reflete uma tendência mais ampla de fechar a lacuna entre o que o sistema de tipos pode expressar e como o Python idiomático é realmente escrito.
Versões de Stack: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ e uv 0.6+.
Revisado por Chris St. John·Última atualização: 15 de jul. de 2026