Type hints (ou anotações de tipo) são uma funcionalidade introduzida no Python 3.5 que permite indicar os tipos esperados de argumentos de funções, valores de retorno e variáveis. Embora o Python continue sendo uma linguagem dinamicamente tipada, as type hints trazem os benefícios da verificação estática de tipos, melhorando a legibilidade, a documentação e a capacidade de detecção de erros antes da execução. Nesta aula, exploraremos como usar anotações de tipo, o módulo typing para tipos mais complexos, por que adotá-las e uma introdução ao mypy para verificação de tipos.

Anotações de tipo

As anotações de tipo permitem especificar o tipo esperado de um parâmetro ou o tipo de retorno de uma função. Elas são escritas usando dois pontos (:) após o nome do parâmetro e uma seta (->) antes do tipo de retorno. Por exemplo:

def saudacao(nome: str) -> str:
    return f"Olá, {nome}!"

Neste exemplo, indicamos que nome deve ser uma string e que a função retorna uma string. Essas anotações não afetam o comportamento em tempo de execução — o Python não verifica os tipos automaticamente. Elas servem principalmente para documentação e para ferramentas externas, como o mypy. Você também pode anotar variáveis:

idade: int = 30
nome: str = "Alice"

As anotações são armazenadas no dicionário __annotations__ do módulo ou função, podendo ser acessadas em tempo de execução, se necessário.

typing (List, Dict, Optional)

Para tipos mais complexos, como listas, dicionários e tipos opcionais, usamos o módulo typing. Ele fornece classes genéricas como List, Dict, Optional, Union, entre outros. Por exemplo:

from typing import List, Dict, Optional

def processar_itens(itens: List[str]) -> None:
    for item in itens:
        print(item)

def obter_config(chave: str) -> Optional[Dict[str, int]]:
    # retorna um dicionário ou None
    config = {"porta": 8080, "timeout": 30}
    return config.get(chave)  # retorna None se chave não existir

List[str] indica uma lista de strings; Dict[str, int] indica um dicionário com chaves string e valores inteiros; Optional[Dict[str, int]] é equivalente a Union[Dict[str, int], None], significando que o valor pode ser o dicionário ou None. Outros tipos úteis incluem Tuple, Set, Any (para qualquer tipo) e Union para múltiplos tipos possíveis.

Por que usar

Usar type hints traz diversos benefícios:

  • Documentação automática: Os tipos servem como documentação viva, facilitando o entendimento do código por outros desenvolvedores (ou por você mesmo no futuro).
  • Detecção precoce de erros: Ferramentas como mypy podem encontrar bugs antes mesmo de executar o código, como passar um inteiro onde se espera uma string.
  • Melhor suporte em IDEs: Editores como VS Code e PyCharm usam type hints para fornecer autocompletar, inferência de tipos e detecção de erros em tempo real.
  • Facilita refatoração: Com tipos claros, alterar o código fica mais seguro, pois você sabe exatamente o que cada função espera e retorna.
  • Padronização: Em projetos grandes, type hints ajudam a manter consistência e reduzir ambiguidades.

Embora exijam um esforço inicial para escrever, os benefícios a longo prazo superam o custo, especialmente em projetos colaborativos ou de longa duração.

mypy (visão geral)

mypy é um verificador de tipos estático para Python. Ele analisa seu código-fonte e relata inconsistências de tipo com base nas anotações. Para instalar, use pip install mypy. Em seguida, execute mypy meu_arquivo.py para verificar o arquivo. Exemplo:

# arquivo: exemplo.py
from typing import List

def soma(numeros: List[int]) -> int:
    return sum(numeros)

resultado = soma([1, 2, 3])
print(resultado)

Ao executar mypy exemplo.py, nenhum erro será reportado. Mas se você alterar a chamada para soma([1, "2", 3]), o mypy indicará um erro de tipo. O mypy suporta configurações via arquivo mypy.ini ou pyproject.toml, permitindo ajustar o rigor da verificação.

Boas práticas

  • Use type hints consistentemente em todo o projeto, especialmente em funções públicas e APIs.
  • Prefira tipos genéricos do módulo typing em vez de tipos como list ou dict para maior clareza (ex.: List[int] vs list).
  • Evite usar Any a menos que seja realmente necessário, pois ele desativa a verificação de tipos.
  • Para valores opcionais, prefira Optional[T] em vez de Union[T, None].
  • Execute o mypy periodicamente (por exemplo, em um hook de pré-commit) para manter a qualidade do código.

Referências

Exercícios

  1. Escreva uma função anotada que receba uma lista de números inteiros e retorne a soma dos quadrados dos números pares.

    ✓ Resposta:
    from typing import List
    
    def soma_quadrados_pares(numeros: List[int]) -> int:
        return sum(x**2 for x in numeros if x % 2 == 0)
  2. Crie uma função que receba um dicionário com chaves string e valores inteiros, e retorne a soma de todos os valores. Use type hints.

    ✓ Resposta:
    from typing import Dict
    
    def soma_valores(dicionario: Dict[str, int]) -> int:
        return sum(dicionario.values())
  3. Implemente uma função que receba um nome (string) e uma idade (int) opcional, e retorne uma saudação personalizada. Se a idade for fornecida, inclua-a na mensagem.

    ✓ Resposta:
    from typing import Optional
    
    def saudacao(nome: str, idade: Optional[int] = None) -> str:
        if idade is not None:
            return f"Olá, {nome}! Você tem {idade} anos."
        return f"Olá, {nome}!"
  4. Escreva uma função que receba uma lista de strings e retorne uma lista com as strings que têm mais de 5 caracteres. Use type hints.

    ✓ Resposta:
    from typing import List
    
    def strings_longas(lista: List[str]) -> List[str]:
        return [s for s in lista if len(s) > 5]
  5. Explique em uma frase o que o mypy faz e como executá-lo em um arquivo chamado app.py.

    ✓ Resposta:

    O mypy é um verificador de tipos estático para Python que analisa o código-fonte em busca de inconsistências de tipo com base nas anotações; para executá-lo no arquivo app.py, use o comando mypy app.py no terminal.