Type hints (ou anotações de tipo) são uma funcionalidade introduzida no Python 3.5 que permite indicar os tipos esperados de variáveis, parâmetros de funções e valores de retorno. Eles não afetam a execução do código, mas ajudam na legibilidade, documentação e na detecção precoce de erros de tipo com ferramentas externas. Esta aula cobre desde a sintaxe básica até o uso do módulo typing e a ferramenta mypy.

Embora o Python continue sendo uma linguagem de tipagem dinâmica, os type hints trazem os benefícios da tipagem estática sem perder a flexibilidade. Eles são especialmente úteis em projetos grandes, onde a clareza e a manutenibilidade são cruciais.

Anotações de tipo

As anotações de tipo são escritas usando a sintaxe variavel: tipo para variáveis e def funcao(param: tipo) -> tipo_retorno: para funções. Elas são opcionais e ignoradas pelo interpretador em tempo de execução.

Exemplo básico:

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

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

print(saudacao(nome))  # Funciona normalmente

As anotações podem ser usadas em qualquer lugar onde uma variável é definida, inclusive em atributos de classe e em loops. No entanto, o uso mais comum é em funções e métodos.

typing (List, Dict, Optional)

O módulo typing fornece tipos genéricos para estruturas de dados como listas, dicionários, tuplas, conjuntos e tipos opcionais. Esses tipos permitem especificar o tipo dos elementos contidos.

Exemplos comuns:

from typing import List, Dict, Optional, Tuple, Set

# Lista de inteiros
numeros: List[int] = [1, 2, 3]

# Dicionário com chave string e valor inteiro
contagem: Dict[str, int] = {"a": 1, "b": 2}

# Tupla de dois elementos: string e float
dado: Tuple[str, float] = ("temperatura", 25.5)

# Conjunto de strings
tags: Set[str] = {"python", "type hint"}

# Tipo opcional: pode ser int ou None
idade: Optional[int] = None

Optional[X] é equivalente a Union[X, None]. Também existem Any (qualquer tipo), Union (união de tipos) e Callable (para funções).

Exemplo de função com tipos complexos:

from typing import List, Optional

def processar_itens(itens: List[str], max_itens: Optional[int] = None) -> List[str]:
    if max_itens is not None:
        return itens[:max_itens]
    return itens

Por que usar

Os type hints trazem diversos benefícios para o desenvolvimento de software em Python:

  • Documentação automática: indicam claramente o que uma função espera e retorna, facilitando o entendimento do código por outros desenvolvedores.
  • Detecção precoce de erros: ferramentas como mypy, Pyright e Pylance podem analisar o código e apontar inconsistências de tipo antes da execução.
  • Melhor suporte em IDEs: editores como VS Code e PyCharm usam type hints para fornecer autocompletar, sugestões e verificação em tempo real.
  • Refatoração segura: com tipos explícitos, é mais fácil modificar o código sabendo quais partes dependem de quais tipos.
  • Redução de bugs: muitos erros comuns, como passar um argumento do tipo errado, podem ser capturados estaticamente.

Embora exijam um esforço inicial, os type hints se pagam rapidamente em projetos de médio e grande porte, melhorando a qualidade e a manutenibilidade do código.

mypy (visão geral)

mypy é uma ferramenta de verificação estática de tipos para Python. Ela analisa o código-fonte e reporta erros de tipo baseados nas anotações fornecidas. mypy suporta a maior parte do módulo typing e pode ser integrada a editores e pipelines de CI/CD.

Instalação e uso básico:

pip install mypy
mypy meu_arquivo.py

Exemplo de código com erro detectado pelo mypy:

# arquivo: exemplo.py
def soma(a: int, b: int) -> int:
    return a + b

resultado = soma(1, "2")  # Erro: argumento 2 deve ser int, não str

Ao executar mypy exemplo.py, a ferramenta indicará o erro:

exemplo.py:4: error: Argument 2 to "soma" has incompatible type "str"; expected "int"

mypy também oferece opções como --strict para habilitar verificações mais rigorosas, e suporte a arquivos de configuração (mypy.ini ou pyproject.toml). É uma ferramenta madura e amplamente adotada na comunidade Python.

Boas práticas

  • Use type hints em todas as funções públicas de módulos e classes.
  • Prefira list[int] em vez de List[int] a partir do Python 3.9 (tipos embutidos genéricos).
  • Evite Any sempre que possível; prefira tipos mais específicos.
  • Mantenha a configuração do mypy no projeto para garantir consistência.
  • Considere usar TypeVar para funções genéricas quando necessário.

Referências

Exercícios

  1. Escreva uma função que receba uma lista de números inteiros e retorne a soma deles, usando type hints.
  2. ✓ Resposta:
    from typing import List
    
    def soma_numeros(numeros: List[int]) -> int:
        return sum(numeros)
  3. Crie uma função que aceite um dicionário com chaves string e valores float, e retorne a média dos valores.
  4. ✓ Resposta:
    from typing import Dict
    
    def media_valores(dados: Dict[str, float]) -> float:
        if not dados:
            return 0.0
        return sum(dados.values()) / len(dados)
  5. Implemente uma função que receba um nome (string) e uma idade opcional (int ou None), e retorne uma saudação personalizada.
  6. ✓ 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}!"
  7. Escreva uma função que receba uma lista de strings e retorne uma nova lista contendo apenas as strings que começam com a letra 'A', usando type hints.
  8. ✓ Resposta:
    from typing import List
    
    def filtrar_a(palavras: List[str]) -> List[str]:
        return [palavra for palavra in palavras if palavra.startswith('A')]
  9. Use o mypy para verificar o código abaixo e corrija os erros de tipo. Escreva a versão corrigida do código com type hints adequados.
  10. ✓ Resposta:
    # Código original (com erros):
    # def multiplicar(a, b):
    #     return a * b
    # resultado = multiplicar(2, "3")
    
    # Versão corrigida:
    from typing import Union
    
    def multiplicar(a: int, b: int) -> int:
        return a * b
    
    resultado = multiplicar(2, 3)  # Agora ambos são int