Pular para o conteúdo

    Capítulo 37, Intermediário

    Type hints

    Anotações de tipo documentam o contrato do código e permitem que ferramentas achem erros antes de rodar. O Python em si ignora as anotações.

    Código deste capítulo: intermediario/cap37_type_hints.py

    Anotar parâmetros e retorno

    A anotação vai depois de dois pontos nos parâmetros e depois de -> no retorno. Elas ficam guardadas em __annotations__, mas o interpretador não as verifica:

    intermediario/cap37_type_hints.pylinhas 10 a 16
    def saudar(nome: str, vezes: int = 1) -> str:
        return (f"Olá, {nome}! " * vezes).strip()
    
    
    print(saudar("Ana", 2))
    print(saudar.__annotations__)
    print(saudar(123))
    
    Saída
    Olá, Ana! Olá, Ana!
    {'nome': <class 'str'>, 'vezes': <class 'int'>, 'return': <class 'str'>}
    Olá, 123!
    

    A última chamada passa um inteiro onde se esperava texto, e o Python roda sem reclamar. Quem acusa o erro é uma ferramenta externa, o verificador de tipos.

    Coleções, opcionais e uniões

    Desde o Python 3.9 você pode usar list[int] e dict[str, int] diretamente. A barra vertical (int | None, desde o 3.10) expressa uma união:

    intermediario/cap37_type_hints.pylinhas 21 a 29
    def media(valores: list[float]) -> float:
        return sum(valores) / len(valores)
    
    
    def buscar(config: dict[str, int], chave: str) -> int | None:
        return config.get(chave)
    
    
    print(media([1.0, 2.0, 3.0]), buscar({"porta": 80}, "host"))
    
    Saída
    2.0 None
    

    Aliases, funções e estruturas

    Para nomear um tipo complexo, o Python 3.12 trouxe a instrução type. Funções como argumento usam Callable. E para registros com forma conhecida há NamedTuple e TypedDict:

    intermediario/cap37_type_hints.pylinhas 34 a 56
    from collections.abc import Callable
    from typing import NamedTuple, TypedDict
    
    type Predicado = Callable[[int], bool]
    
    
    def filtrar(numeros: list[int], teste: Predicado) -> list[int]:
        return [n for n in numeros if teste(n)]
    
    
    class Ponto(NamedTuple):
        x: int
        y: int
    
    
    class Usuario(TypedDict):
        nome: str
        idade: int
    
    
    u: Usuario = {"nome": "Ana", "idade": 30}
    print(filtrar([1, 2, 3, 4], lambda n: n % 2 == 0))
    print(Ponto(1, 2), u)
    
    Saída
    [2, 4]
    Ponto(x=1, y=2) {'nome': 'Ana', 'idade': 30}
    

    A constante "de verdade" para ferramentas de tipo é typing.Final: LIMITE: Final = 3 avisa que reatribuir é um erro.

    Quem verifica

    O verificador mais usado é o mypy. O pyright é outra boa opção. Os dois leem o código sem executá-lo e apontam inconsistências. Instale como ferramenta de desenvolvimento do projeto:

    Terminal
    uv add --dev mypy
    uv run mypy exemplos/tipos/erro_de_tipo.py
    

    O arquivo a verificar chama uma função com o tipo errado:

    exemplos/tipos/erro_de_tipo.py
    def dobrar(valor: int) -> int:
        return valor * 2
    
    
    dobrar("3")
    
    Saída
    exemplos/tipos/erro_de_tipo.py:5: error: Argument 1 to "dobrar" has incompatible type "str"; expected "int"  [arg-type]
    Found 1 error in 1 file (checked 1 source file)
    

    Referências a tipos ainda não definidos

    Antes do Python 3.14, anotar com uma classe definida mais abaixo exigia aspas ("Cliente") ou from __future__ import annotations. A partir do 3.14, as anotações são avaliadas de forma adiada por padrão, e esse problema desaparece. Se o seu projeto ainda suporta versões anteriores, mantenha o __future__.

    Exercício 1

    Anote uma função de agrupamento

    Escreva agrupar_por_inicial(palavras: list[str]) -> dict[str, list[str]], que agrupe as palavras pela primeira letra em minúscula.