Type hints rigorosos: PEP 695, Protocol, TypedDict
⏱ 13 min·⭐ 55 XP
Pré-requisitos (0/1)0%
- ⬜⚡ uv e Python moderno: chega de pip + venv manual(Python para Engenheiros)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
PEP 695 generics (3.12+)
# Antes (legacy)
from typing import TypeVar
T = TypeVar("T")
def first(xs: list[T]) -> T | None:
return xs[0] if xs else None
# Agora (PEP 695)
def first[T](xs: list[T]) -> T | None:
return xs[0] if xs else None
# Classes
class Stack[T]:
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None: self._items.append(item)
def pop(self) -> T | None: return self._items.pop() if self._items else None
# Aliases
type Result[T] = tuple[bool, T | None]Protocol — structural typing
| Recurso | Verificado em | Serve para | Armadilha |
|---|---|---|---|
| Anotação simples | Analisador estático | Documentar e checar antes de rodar | Não existe em execução — não valida entrada externa |
| Protocolo | Analisador estático | Aceitar qualquer objeto com a forma exigida | Confundir com classe base: não há herança envolvida |
| Dicionário tipado | Analisador estático | Descrever a forma de um dicionário que continua dicionário | Nada é instanciado nem validado — é anotação, não modelo |
| Tipo anotado com metadado | Analisador + biblioteca que o lê | Juntar tipo e regra de validação num nome só | Sem a biblioteca, o metadado é inerte |
| Modelo de validação | Execução | Validar de verdade o que vem de fora | Custa processamento — não use para estrutura interna que já é confiável |
RecursoAnotação simples
Verificado emAnalisador estático
Serve paraDocumentar e checar antes de rodar
ArmadilhaNão existe em execução — não valida entrada externa
RecursoProtocolo
Verificado emAnalisador estático
Serve paraAceitar qualquer objeto com a forma exigida
ArmadilhaConfundir com classe base: não há herança envolvida
RecursoDicionário tipado
Verificado emAnalisador estático
Serve paraDescrever a forma de um dicionário que continua dicionário
ArmadilhaNada é instanciado nem validado — é anotação, não modelo
RecursoTipo anotado com metadado
Verificado emAnalisador + biblioteca que o lê
Serve paraJuntar tipo e regra de validação num nome só
ArmadilhaSem a biblioteca, o metadado é inerte
RecursoModelo de validação
Verificado emExecução
Serve paraValidar de verdade o que vem de fora
ArmadilhaCusta processamento — não use para estrutura interna que já é confiável
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None: ...
def safe_close(resource: SupportsClose) -> None:
resource.close()
# Qualquer classe com .close() passa — sem herdar
class File:
def close(self) -> None: print("closed")
class DBConnection:
def close(self) -> None: print("db closed")
safe_close(File())
safe_close(DBConnection()) # ambos OK, zero herançaTypedDict + NotRequired
from typing import TypedDict, NotRequired
class UserDict(TypedDict):
id: str
email: str
name: NotRequired[str] # opcional
def handle(user: UserDict) -> None:
print(user["email"])
if "name" in user:
print(user["name"])Quiz rápido
`Protocol` permite tipagem estrutural. O que isso muda na prática em relação a exigir uma classe base?
mypy strict em CI
# pyproject.toml
[tool.mypy]
python_version = "3.12"
strict = true
# strict = disallow_untyped_defs + check_untyped_defs + warn_unused + etc.
# CI
# - name: Type check
# run: uv run mypy src/💡
pyright (Microsoft) é mais rápido que mypy e roda no VSCode por default (via Pylance). Use pyright no editor, mypy no CI (ou só pyright se time alinha).
Annotated + metadata
from typing import Annotated
from pydantic import Field
# Tipo + validação FastAPI/Pydantic inline
UserId = Annotated[str, Field(pattern=r"^u_[a-z0-9]+$")]
def get_user(id: UserId) -> User: ...Perguntas frequentes
❓ Como tipar interface sem herança em Python?
Com protocolo, que descreve a forma esperada e é satisfeito por qualquer objeto compatível — tipagem estrutural, como em TypeScript. É o que permite tipar sem obrigar o autor da classe a herdar de uma base sua, e é o mecanismo mais subutilizado da tipagem em Python.
❓ Para que serve dicionário tipado?
Para descrever a forma de um dicionário com chaves conhecidas — típico de resposta de API e de configuração. É a alternativa a criar classe quando o dado só transita, e o verificador passa a cobrar chave e tipo, que é o que faltava.
❓ Vale ligar o modo mais estrito no verificador?
Vale, gradualmente por módulo, começando pelo código novo. Ligar no repositório inteiro de uma vez produz centenas de erros e a decisão de desligar. A adoção por módulo com portão no fluxo de integração é a que se sustenta.
Fixando
Quiz rápido
Quando `TypedDict` é a escolha certa em vez de um modelo de dados?
Quiz rápido
O módulo sugere `pyright` no editor e `mypy` no CI. Que problema essa divisão resolve?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…