<h2>Introdução ao Mypy: Por Que Type Checking Importa</h2>
<p>Quando você trabalha em projetos Python de médio a grande porte, a falta de verificação de tipos pode se tornar um pesadelo. Python é dinamicamente tipado, o que significa que os tipos são verificados apenas em tempo de execução. Isso é ótimo para prototipagem rápida, mas desastroso para produção quando um erro de tipo chega ao usuário final.</p>
<p>Mypy é uma ferramenta de verificação estática de tipos que analisa seu código antes da execução, identificando incompatibilidades de tipo sem rodar uma única linha. Ele usa as <strong>type hints</strong> — anotações de tipo que você adiciona ao código — para garantir que você está usando funções, variáveis e objetos corretamente. Não é um validador de lógica, mas é extremamente eficaz em pegar bugs silenciosos causados por tipos incorretos.</p>
<h2>Fundamentos: Type Hints e Anotações de Tipo</h2>
<h3>O que são Type Hints?</h3>
<p>Type hints são simplesmente anotações que informam qual tipo de dado uma variável, parâmetro ou retorno de função deve ter. Elas são completamente opcionais em Python — o código roda normalmente sem elas — mas são essenciais para o Mypy funcionar.</p>
<p>Vamos começar simples:</p>
<pre><code class="language-python"># Sem type hints (código válido, mas sem informação de tipo)
def saudacao(nome):
return f"Olá, {nome}!"
Com type hints (código idêntico, mas com informação)
def saudacao(nome: str) -> str:
return f"Olá, {nome}!"</code></pre>
<p>No segundo caso, estamos dizendo: "o parâmetro <code>nome</code> deve ser uma string, e a função retorna uma string". Se alguém chamar <code>saudacao(123)</code>, o Mypy vai reclamar, mesmo que Python permita.</p>
<h3>Tipos Básicos e Estruturas Complexas</h3>
<p>Os tipos mais comuns vêm direto de Python: <code>int</code>, <code>str</code>, <code>bool</code>, <code>float</code>. Mas frequentemente você trabalha com coleções e estruturas mais complexas. Para isso, usamos o módulo <code>typing</code>:</p>
<pre><code class="language-python">from typing import List, Dict, Optional, Union, Tuple
Lista de inteiros
numeros: List[int] = [1, 2, 3, 4]
Dicionário com chaves string e valores inteiros
idades: Dict[str, int] = {"Alice": 30, "Bob": 25}
Tipo que pode ser None (None é um tipo válido)
resultado: Optional[str] = None
Um tipo ou outro
status: Union[int, str] = "ativo"
Tupla com tipos específicos
coordenadas: Tuple[float, float] = (10.5, 20.3)</code></pre>
<p>Quando você usa <code>Optional[str]</code>, está dizendo que o valor pode ser uma string <strong>ou</strong> <code>None</code>. Sem essa anotação, código como <code>valor = funcao_que_retorna_none()</code> e depois <code>print(valor.upper())</code> passaria despercebido e quebraria em produção.</p>
<h2>Mypy na Prática: Configuração e Execução</h2>
<h3>Instalação e Setup Básico</h3>
<p>Mypy é uma ferramenta independente do Python. Instale via pip:</p>
<pre><code class="language-bash">pip install mypy</code></pre>
<p>Depois, execute sobre seu código:</p>
<pre><code class="language-bash">mypy seu_arquivo.py</code></pre>
<p>Para verificar um projeto inteiro:</p>
<pre><code class="language-bash">mypy .</code></pre>
<p>Mypy criará um arquivo <code>.mypy_cache/</code> para otimizar futuras análises. É seguro adicionar isso ao <code>.gitignore</code>.</p>
<h3>Configuração com pyproject.toml</h3>
<p>Para projetos profissionais, você quer configurar regras padrão. Crie um arquivo <code>pyproject.toml</code> na raiz:</p>
<pre><code class="language-toml">[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict = true</code></pre>
<p>Essas configurações ativam o modo "strict", que é a forma mais rigorosa. <code>disallow_untyped_defs = true</code> força você a anotar <strong>todas</strong> as funções. Isso parece restritivo, mas é exatamente o que projetos sérios precisam.</p>
<h3>Um Exemplo Real: Sistema de Cadastro</h3>
<p>Vamos criar um exemplo realista com problemas que Mypy detecta:</p>
<pre><code class="language-python">from typing import List, Optional
from dataclasses import dataclass
@dataclass
class Usuario:
id: int
nome: str
email: str
idade: Optional[int] = None
class Banco:
def __init__(self) -> None:
self.usuarios: List[Usuario] = []
def adicionar(self, usuario: Usuario) -> None:
"""Adiciona um usuário ao banco."""
self.usuarios.append(usuario)
def buscar_por_id(self, id: int) -> Optional[Usuario]:
"""Retorna o usuário com o ID especificado ou None."""
for usuario in self.usuarios:
if usuario.id == id:
return usuario
return None
def listar_emails(self) -> List[str]:
"""Retorna lista de todos os emails."""
return [u.email for u in self.usuarios]
Uso correto
banco = Banco()
novo_usuario = Usuario(id=1, nome="Alice", email="alice@example.com", idade=30)
banco.adicionar(novo_usuario)
usuario_encontrado = banco.buscar_por_id(1)
if usuario_encontrado:
print(usuario_encontrado.nome)</code></pre>
<p>Se você tentar fazer algo errado, Mypy avisa:</p>
<pre><code class="language-python"></code></pre>
<h2>Padrões Avançados: Generics, Protocolos e Type Aliases</h2>
<h3>Generics: Escrevendo Código Reutilizável com Tipos</h3>
<p>Muitas vezes você quer escrever funções ou classes que funcionam com qualquer tipo, mas ainda quer segurança de tipos. Para isso, usamos <strong>type variables</strong>:</p>
<pre><code class="language-python">from typing import TypeVar, List, Generic
T = TypeVar('T') # Um tipo genérico que pode ser qualquer coisa
def primeira_elemento(lista: List[T]) -> Optional[T]:
"""Retorna o primeiro elemento da lista, ou None se vazia."""
if lista:
return lista[0]
return None
Mypy entende que se você passa List[int], retorna Optional[int]
resultado_int = primeira_elemento([1, 2, 3]) # tipo: Optional[int]
resultado_str = primeira_elemento(["a", "b"]) # tipo: Optional[str]</code></pre>
<p>Você também pode criar classes genéricas:</p>
<pre><code class="language-python">from typing import Generic, TypeVar
T = TypeVar('T')
class Caixa(Generic[T]):
"""Uma caixa que armazena um item de qualquer tipo."""
def __init__(self, conteudo: T) -> None:
self.conteudo = conteudo
def obter(self) -> T:
return self.conteudo
Mypy entende os tipos específicos
caixa_numero = Caixa(42)
valor = caixa_numero.obter() # tipo: int
caixa_texto = Caixa("Python")
texto = caixa_texto.obter() # tipo: str</code></pre>
<h3>Protocolos: Interfaces Estruturais</h3>
<p>Às vezes você quer dizer "qualquer objeto que tenha esses métodos" sem se importar com herança. Isso é um <strong>Protocol</strong>:</p>
<pre><code class="language-python">from typing import Protocol
class Persistivel(Protocol):
"""Qualquer coisa que possa ser salva."""
def salvar(self) -> None: ...
class Documento:
def salvar(self) -> None:
print("Documento salvo em disco")
class Email:
def salvar(self) -> None:
print("Email arquivado")
def arquivar(item: Persistivel) -> None:
"""Aceita qualquer objeto com método salvar()."""
item.salvar()
Funciona com Documento e Email sem herança explícita
arquivar(Documento())
arquivar(Email())</code></pre>
<h3>Type Aliases: Nomes Legíveis para Tipos Complexos</h3>
<p>Quando você tem tipos complexos, crie aliases:</p>
<pre><code class="language-python">from typing import Dict, List, Tuple
Tipo complexo sem alias (ilegível)
def processar_dados(dados: Dict[str, List[Tuple[int, str]]]) -> None:
pass
Com alias (muito melhor)
Registro = Tuple[int, str]
Tabela = Dict[str, List[Registro]]
def processar_dados(dados: Tabela) -> None:
pass</code></pre>
<h2>Integração em Projetos Reais: CI/CD e Boas Práticas</h2>
<h3>Mypy no Pipeline de CI/CD</h3>
<p>Adicione Mypy ao seu pipeline de testes. Exemplo com GitHub Actions:</p>
<pre><code class="language-yaml">name: Type Check
on: [push, pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.10"
- run: pip install mypy
- run: mypy src/</code></pre>
<p>Agora todo PR que quebra os tipos será rejeitado automaticamente.</p>
<h3>Suppressões Controladas</h3>
<p>Às vezes você precisa ignorar um erro de tipo por uma razão legítima. Use <code># type: ignore</code>:</p>
<pre><code class="language-python"># Ignorar um erro específico na linha
dados = json.loads(entrada) # type: ignore[arg-type]
Ignorar toda a linha
funcao_antiga_sem_tipos() # type: ignore
Ignorar um bloco inteiro
mypy: ignore-errors
def codigo_legado():
pass</code></pre>
<p>Use com moderação! Cada <code># type: ignore</code> deve ter um comentário explicando o porquê.</p>
<h3>Estratégia de Adoção Gradual</h3>
<p>Se você herdou um codebase sem tipos, não precisa anotar tudo de uma vez. Use <code># mypy: allow-untyped-defs</code> para arquivos específicos:</p>
<pre><code class="language-python"># mypy: allow-untyped-defs
Este arquivo é legado. Anotaremos gradualmente.
def funcao_antiga(x):
return x * 2
def funcao_nova(valor: int) -> int: # Novas funções com tipos
return valor * 2</code></pre>
<h2>Conclusão</h2>
<p>Você aprendeu que <strong>Mypy traz confiabilidade a projetos Python através de verificação estática de tipos</strong>, permitindo detectar bugs antes da execução sem adicionar overhead em runtime. Em segundo lugar, <strong>type hints não são apenas anotações cosméticas — eles documentam contratos de código e permitem que ferramentas como IDEs e Mypy façam seu trabalho</strong>. Por fim, <strong>a adoção de Mypy em projetos profissionais é progressiva</strong>: comece com o modo padrão, evolua para o modo strict, e integre ao CI/CD para garantir qualidade consistente.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://mypy.readthedocs.io/" target="_blank" rel="noopener noreferrer">Documentação Oficial do Mypy</a></li>
<li><a href="https://www.python.org/dev/peps/pep-0484/" target="_blank" rel="noopener noreferrer">PEP 484 - Type Hints</a></li>
<li><a href="https://realpython.com/python-type-checking/" target="_blank" rel="noopener noreferrer">Real Python - Type Checking in Python</a></li>
<li><a href="https://docs.python.org/3/library/typing.html" target="_blank" rel="noopener noreferrer">Documentação do módulo typing</a></li>
<li><a href="https://google.github.io/styleguide/pyguide.html#type-comments" target="_blank" rel="noopener noreferrer">Google Python Style Guide - Type Comments</a></li>
</ul>
<p><!-- FIM --></p>