Boas Praticas Python para 2026
As melhores praticas de Python atualizadas para 2026. Tipagem, ferramentas modernas, patterns, performance e o que ha de novo na linguagem.
O ecossistema Python evolui rapidamente, e as boas praticas de hoje refletem anos de aprendizado da comunidade. Com Python 3.13 estavel e o 3.14 no horizonte, temos novas ferramentas, padroes e abordagens que elevam a qualidade do codigo. Neste artigo, reunimos as praticas mais importantes que todo desenvolvedor Python deve adotar em 2026.
Use Tipagem Estatica Desde o Inicio
Tipagem estatica deixou de ser opcional em projetos profissionais. Com o amadurecimento do mypy, pyright e as melhorias nativas da linguagem, tipar seu codigo e tao natural quanto escrever docstrings:
from dataclasses import dataclass
from typing import Protocol
# Tipos claros e expressivos
@dataclass
class Pedido:
cliente_id: int
itens: list[str]
valor_total: float
desconto: float = 0.0
@property
def valor_final(self) -> float:
return self.valor_total * (1 - self.desconto / 100)
# Protocolos para tipagem estrutural
class Repositorio(Protocol):
def salvar(self, entidade: dict) -> int: ...
def buscar(self, id: int) -> dict | None: ...
def listar(self, filtros: dict) -> list[dict]: ...
def processar_pedido(
pedido: Pedido,
repo: Repositorio,
notificar: bool = True,
) -> int:
"""Processa e salva um pedido. Tipos claros no contrato."""
dados = {
"cliente": pedido.cliente_id,
"valor": pedido.valor_final,
"itens": pedido.itens,
}
pedido_id = repo.salvar(dados)
return pedido_id
Ferramentas Modernas de Desenvolvimento
O ecossistema de ferramentas Python amadureceu significativamente. Em 2026, a stack recomendada inclui:
# pyproject.toml - configuracao centralizada
[project]
name = "meu-projeto"
version = "1.0.0"
requires-python = ">=3.12"
[tool.ruff]
target-version = "py312"
line-length = 100
select = ["E", "F", "I", "N", "W", "UP", "B", "SIM"]
[tool.ruff.format]
quote-style = "double"
[tool.mypy]
python_version = "3.12"
strict = true
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short --cov=src"
O Ruff substituiu flake8, isort e black em um unico tool, executando ordens de magnitude mais rapido:
# Ruff faz tudo: lint + format
ruff check src/ --fix
ruff format src/
# uv: gerenciador de pacotes ultrarapido
uv pip install -r requirements.txt
uv venv
uv run pytest
Estruture Projetos com Clareza
A organizacao do projeto impacta diretamente a produtividade da equipe:
meu-projeto/
src/
meu_projeto/
__init__.py
core/
__init__.py
models.py
services.py
exceptions.py
api/
__init__.py
routes.py
schemas.py
infra/
__init__.py
database.py
cache.py
tests/
unit/
integration/
conftest.py
pyproject.toml
# src/meu_projeto/core/exceptions.py
class AppError(Exception):
"""Excecao base da aplicacao."""
def __init__(self, mensagem: str, codigo: str = "ERRO_GENERICO"):
self.mensagem = mensagem
self.codigo = codigo
super().__init__(mensagem)
class NaoEncontradoError(AppError):
def __init__(self, recurso: str, id: int):
super().__init__(
mensagem=f"{recurso} com ID {id} nao encontrado",
codigo="NAO_ENCONTRADO",
)
class ValidacaoError(AppError):
def __init__(self, campo: str, detalhe: str):
super().__init__(
mensagem=f"Erro de validacao em '{campo}': {detalhe}",
codigo="VALIDACAO_FALHOU",
)
Prefira Composicao a Heranca
Heranca profunda cria acoplamento e dificulta testes. Em 2026, composicao e a abordagem preferida:
from dataclasses import dataclass, field
from typing import Callable
# Em vez de heranca profunda
@dataclass
class Validador:
regras: list[Callable[[str], bool]] = field(default_factory=list)
def adicionar_regra(self, regra: Callable[[str], bool]):
self.regras.append(regra)
return self # Fluent interface
def validar(self, valor: str) -> tuple[bool, list[str]]:
erros = []
for regra in self.regras:
try:
if not regra(valor):
erros.append(f"Regra {regra.__name__} falhou")
except Exception as e:
erros.append(str(e))
return len(erros) == 0, erros
# Regras como funcoes simples
def nao_vazio(valor: str) -> bool:
return len(valor.strip()) > 0
def tamanho_minimo(min_chars: int) -> Callable[[str], bool]:
def regra(valor: str) -> bool:
return len(valor) >= min_chars
regra.__name__ = f"tamanho_minimo_{min_chars}"
return regra
def sem_caracteres_especiais(valor: str) -> bool:
return valor.isalnum()
# Uso: composicao flexivel
validador_nome = (
Validador()
.adicionar_regra(nao_vazio)
.adicionar_regra(tamanho_minimo(3))
)
valido, erros = validador_nome.validar("AB")
print(f"Valido: {valido}, Erros: {erros}")
Escreva Testes Expressivos
Testes bem escritos servem como documentacao viva do sistema:
import pytest
from meu_projeto.core.services import PedidoService
from meu_projeto.core.exceptions import ValidacaoError
class TestPedidoService:
"""Testes do servico de pedidos."""
@pytest.fixture
def servico(self):
repo_fake = RepositorioEmMemoria()
return PedidoService(repositorio=repo_fake)
def test_cria_pedido_com_dados_validos(self, servico):
pedido = servico.criar(
cliente_id=1,
itens=["Notebook", "Mouse"],
valor=4599.90,
)
assert pedido.cliente_id == 1
assert len(pedido.itens) == 2
assert pedido.valor_total == 4599.90
def test_rejeita_pedido_sem_itens(self, servico):
with pytest.raises(ValidacaoError, match="itens"):
servico.criar(cliente_id=1, itens=[], valor=100)
def test_aplica_desconto_corretamente(self, servico):
pedido = servico.criar(
cliente_id=1,
itens=["Produto"],
valor=1000.0,
desconto=15,
)
assert pedido.valor_final == 850.0
@pytest.mark.parametrize("desconto,esperado", [
(0, 1000.0),
(10, 900.0),
(50, 500.0),
(100, 0.0),
])
def test_descontos_variados(self, servico, desconto, esperado):
pedido = servico.criar(
cliente_id=1,
itens=["Produto"],
valor=1000.0,
desconto=desconto,
)
assert pedido.valor_final == esperado
Performance: Metricas Antes de Otimizar
Nunca otimize sem medir. Use as ferramentas certas para identificar gargalos:
import cProfile
import pstats
from functools import lru_cache
from time import perf_counter
# Decorator para medir tempo
def medir_tempo(func):
def wrapper(*args, **kwargs):
inicio = perf_counter()
resultado = func(*args, **kwargs)
tempo = perf_counter() - inicio
print(f"{func.__name__}: {tempo:.4f}s")
return resultado
return wrapper
# Cache para funcoes puras
@lru_cache(maxsize=256)
def calcular_frete(cep_origem: str, cep_destino: str, peso_kg: float) -> float:
"""Calculo de frete com cache (funcao pura)."""
distancia = abs(int(cep_origem[:5]) - int(cep_destino[:5]))
return round(distancia * 0.01 * peso_kg, 2)
# Profiling
@medir_tempo
def processar_lote(dados: list[dict]) -> list[dict]:
return [transformar(item) for item in dados]
# Para profiling detalhado
def profile_funcao():
profiler = cProfile.Profile()
profiler.enable()
processar_lote(gerar_dados(10000))
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats(20)
Context Managers para Recursos
Garanta que recursos sejam sempre liberados corretamente:
from contextlib import contextmanager
import time
@contextmanager
def transacao(conexao):
"""Context manager para transacoes de banco."""
cursor = conexao.cursor()
try:
yield cursor
conexao.commit()
except Exception:
conexao.rollback()
raise
finally:
cursor.close()
@contextmanager
def timer(operacao: str):
"""Mede e registra o tempo de uma operacao."""
inicio = time.perf_counter()
yield
duracao = time.perf_counter() - inicio
print(f"{operacao}: {duracao:.3f}s")
# Uso
with timer("Processamento de dados"):
resultado = processar_dados_pesados()
Logging Estruturado
Em 2026, logging estruturado com JSON e o padrao para aplicacoes em producao:
import logging
import json
from datetime import datetime
class JsonFormatter(logging.Formatter):
def format(self, record):
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
"function": record.funcName,
}
if record.exc_info:
log_entry["exception"] = self.formatException(record.exc_info)
return json.dumps(log_entry)
# Configuracao
logger = logging.getLogger("meu_projeto")
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# Uso
logger.info("Pedido processado", extra={"pedido_id": 123, "valor": 599.90})
Conclusao
As boas praticas de Python para 2026 refletem um ecossistema maduro e exigente. Tipagem estatica, ferramentas como Ruff e uv, testes expressivos, composicao sobre heranca e logging estruturado nao sao mais diferenciais, sao requisitos basicos para projetos profissionais. Adote essas praticas gradualmente, comecando pelo que traz mais impacto no seu contexto, e seu codigo estara preparado para o futuro.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português