Módulo 2 - Tipagem estática
typing: coleções, Optional e Union
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Anotar coleções com list[int], dict[str, int] e tuple.
- Marcar valores que podem faltar com Optional e X | None.
- Aceitar mais de um tipo com Union e a sintaxe com barra.
- Escolher entre a notação antiga do typing e a moderna com minúsculas.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: typing: coleções, Optional e Union.
Os objetivos desta aula. Anotar coleções com list[int], dict[str, int] e tuple. Marcar valores que podem faltar com Optional e X | None. Aceitar mais de um tipo com Union e a sintaxe com barra. Escolher entre a notação antiga do typing e a moderna com minúsculas.
Veja o essencial, parte por parte.
Anotando listas, dicionários e tuplas. Uma lista de inteiros é list[int]; um dicionário de texto para número é dict[str, int].
Valores que podem faltar ou ter vários tipos. Quando o tipo é X | None, o mypy cobra que você verifique None antes de usar o valor.
Notação antiga e moderna lado a lado. A tabela resume o essencial.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Anotando listas, dicionários e tuplas
Anotar uma coleção não basta dizer que é uma lista; o interessante é dizer do que ela é feita. Você escreve o tipo do contêiner e, entre colchetes, o tipo dos itens. Uma lista de inteiros é list[int]. Um dicionário que mapeia nomes a idades é dict[str, int], porque a chave é texto e o valor é número. Uma tupla com posições de tipos diferentes é tuple[str, int, bool], já que cada posição tem o seu tipo. Se a tupla for uma sequência homogênea de tamanho qualquer, usa-se as reticências: tuple[int, ...].
def somar_todos(numeros: list[int]) -> int:
return sum(numeros)
def idades_por_nome() -> dict[str, int]:
return {"Ana": 30, "Beto": 25}
def coordenada() -> tuple[float, float]:
return (2.5, 4.0)
print(somar_todos([1, 2, 3])) # 6
print(idades_por_nome()) # {'Ana': 30, 'Beto': 25}
print(coordenada()) # (2.5, 4.0)list[int], dict[str, int] e tuple[float, float]: o tipo do conteúdo fica explícito.
Essa notação com colchetes, em minúsculas, é a forma moderna e vale a partir do Python 3.9. Antes era preciso importar List, Dict e Tuple com maiúscula do módulo typing. Você ainda vai encontrar código antigo assim, então é bom reconhecer a forma antiga, mas em código novo prefira as minúsculas, que são mais limpas e não exigem import.
Valores que podem faltar ou ter vários tipos
Duas situações aparecem o tempo todo. A primeira: um valor que pode existir ou não. Uma função de busca devolve o item encontrado ou None quando não acha nada. Isso é um Optional: Optional[str] significa texto ou None. A segunda: um valor que pode ser de mais de um tipo. Uma função que aceita um identificador tanto como número quanto como texto usa um Union: Union[int, str]. Desde o Python 3.10 há uma escrita mais curta com a barra vertical: int | None no lugar de Optional[int], e int | str no lugar de Union[int, str].
def buscar_usuario(id: int) -> str | None:
banco = {1: "Ana", 2: "Beto"}
return banco.get(id) # devolve o nome ou None
def normalizar_id(valor: int | str) -> str:
return str(valor)
nome = buscar_usuario(1)
if nome is not None:
print(nome.upper()) # ANA
print(buscar_usuario(99)) # None
print(normalizar_id(42)) # 42str | None marca um valor que pode faltar; int | str aceita dois tipos.
Notação antiga e moderna lado a lado
| Intenção | Forma antiga (typing) | Forma moderna |
|---|---|---|
| Lista de inteiros | List[int] | list[int] |
| Dicionário texto->número | Dict[str, int] | dict[str, int] |
| Valor opcional | Optional[str] | str | None |
| Vários tipos | Union[int, str] | int | str |
A forma moderna dispensa import e é a recomendada em código novo.
A tabela resume o essencial. Em código novo, prefira a coluna da direita: tipos embutidos em minúsculas e a barra vertical para opcional e união. Ela é mais curta e não exige importar nada. A coluna do meio ainda aparece em bibliotecas e projetos antigos, então reconheça-a quando ler código dos outros. As duas descrevem exatamente a mesma coisa para o mypy; a diferença é só de escrita e de versão mínima do Python.
Com coleções, opcionais e uniões você já cobre a maioria das anotações do dia a dia. O que falta são os tipos que descrevem comportamento, e não só dados: uma função passada como argumento, um tipo genérico que funciona para qualquer conteúdo, e a ideia de aceitar qualquer objeto que tenha certos métodos. É o assunto das próximas aulas, começando por Callable e Any.
Teste rápido
Uma função de busca devolve o nome encontrado ou None. Qual anotação de retorno é a correta?
Perguntas frequentes
- Qual a diferença entre Optional[int] e int | None?
- Nenhuma no significado: ambas dizem inteiro ou None. Optional vem do módulo typing e exige import; a barra vertical, disponível a partir do Python 3.10, dispensa import e é mais curta. Em código novo prefira int | None; reconheça Optional ao ler código mais antigo.
- Posso escrever list sem dizer o tipo dos itens?
- Pode, mas perde valor. Um list cru significa lista de qualquer coisa, e o mypy verifica pouco. Anotar list[int] ou list[str] deixa o mypy conferir se você está colocando e tirando o tipo certo. Sempre que souber o tipo dos itens, informe.
- Como anoto uma tupla que sempre tem exatamente três floats?
- Com tuple[float, float, float], uma posição para cada float. Se a tupla tiver tamanho variável mas itens do mesmo tipo, use as reticências: tuple[float, ...]. A diferença é que a primeira descreve posições fixas e a segunda uma sequência homogênea de tamanho livre.
- Union pode ter mais de dois tipos?
- Sim. Union[int, str, float] aceita qualquer um dos três, e a escrita moderna equivalente é int | str | float. Ainda assim, uma união com muitos tipos costuma ser sinal de que o desenho pode melhorar; use com parcimônia e prefira poucos tipos bem escolhidos.
- Por que o mypy me obriga a checar None antes de usar o valor?
- Porque um valor X | None pode ser None, e chamar um método em None gera erro em execução. Ao exigir um if valor is not None antes do uso, o mypy garante que você tratou o caso vazio. É a proteção contra o famoso erro de NoneType que acontece sem tipos.
- Preciso importar algo para usar dict[str, int]?
- Não, a partir do Python 3.9. Os tipos embutidos em minúsculas, como list, dict e tuple, aceitam colchetes diretamente, sem import. Só a forma antiga com maiúscula, Dict, List, Tuple, exigia importar do typing. Em versões atuais, escreva em minúsculas e siga.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.