🌗 ← Índice

🐍 Python P25 — Boas Práticas (PEP8, typing, docstrings, logging)


Data: 14/08/2026

Nível: Intermediário-Avançado (Mês 3 — Projetos Reais)




🎯 Objetivos





📖 Teoria


PEP 8 — as regras de ouro


RegraExemplo
4 espaços por indentação`def funcao():` + 4 espaços
Máx 79/99 caracteres por linhaquebre com parênteses
Nomes: `snake_case` (funções/var), `CamelCase` (classes), `UPPER_CASE` (constantes)`def calcular_total()`, `class Cliente`, `MAX_TENTATIVAS = 3`
2 linhas em branco entre funções de topo; 1 entre métodos—
Espaços ao redor de operadores`a = b + c` (não `a=b+c`)
Importações: uma por linha, ordenadas`import os` + `from pathlib import Path`


# Ferramentas para verificar:
pip install black ruff
black meu_script.py     # formata automaticamente
ruff check meu_script.py # aponta problemas

Type hints



def calcular_total(preco: float, quantidade: int, desconto: float = 0.0) -> float:
    """Calcula o total com desconto."""
    return preco * quantidade * (1 - desconto)


def buscar(nome: str) -> dict | None:      # Python 3.10+ (ou Optional[dict])
    ...

def processar(itens: list[str]) -> list[int]:
    ...

SituaçãoAnotação
Pode ser nulo`str \None` ou `Optional[str]`
Lista de X`list[X]`
Dict`dict[str, int]`
Tupla`tuple[int, int]`
Qualquer`Any` (evite)

Docstrings (padrão Google)



def enviar_email(destino: str, assunto: str, corpo: str) -> bool:
    """Envia um e-mail.

    Args:
        destino: endereço do destinatário.
        assunto: título do e-mail.
        corpo: conteúdo em texto puro.

    Returns:
        True se enviado com sucesso, False caso contrário.

    Raises:
        ValueError: se o destino for inválido.
    """
    if "@" not in destino:
        raise ValueError(f"E-mail inválido: {destino}")
    ...

💡 Docstrings de módulo/classes/funções públicas = documentação automática (pydoc, Sphinx).

`logging` — o print profissional



import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
)

logger = logging.getLogger(__name__)

logger.debug("Detalhe de depuração")     # só com level=DEBUG
logger.info("Processo iniciado")
logger.warning("Valor incomum detectado")
logger.error("Falha ao conectar")
logger.critical("Sistema parando")


2026-08-14 15:30:00 | INFO | __main__ | Processo iniciado

💡 Use `logger` no lugar de `print` em aplicações reais: níveis, timestamps, filtros e saída em arquivo.

Log em arquivo



import logging

logging.basicConfig(
    level=logging.INFO,
    filename="app.log",
    format="%(asctime)s | %(levelname)s | %(message)s",
)

Estrutura de projeto modular



projeto/
├── main.py               # CLI/ponto de entrada (fino)
├── servicos/
│   ├── __init__.py
│   ├── api.py            # acesso a APIs
│   └── banco.py          # banco de dados
├── modelos.py            # classes de dados
├── utils.py              # helpers
├── tests/
│   └── test_api.py
├── requirements.txt
└── .env

Regra prática: main.py só orquestra; a lógica fica em módulos importáveis (e testáveis).




✍️ Exercícios


  1. 1. Rode `ruff` num script seu e corrija os avisos apontados.
  2. 2. Adicione type hints a uma função da Aula P15 (`ClienteGitHub.buscar_repos`).
  3. 3. Substitua os `print` de um script antigo por `logger.info` / `logger.error`.



🧪 Desafio


Refatore o alerta_disco.py (Aula P24) com boas práticas:





✅ Correção (exemplo)



"""Alerta de disco da VPS via Telegram."""
import logging
import os
import shutil
from dotenv import load_dotenv
import requests

load_dotenv()

TOKEN = os.getenv("TELEGRAM_TOKEN")
CHAT_ID = int(os.getenv("TELEGRAM_CHAT_ID", "0"))
LIMITE_PERCENTUAL = float(os.getenv("LIMITE_DISCO", "80"))

logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
logger = logging.getLogger(__name__)


def uso_disco(caminho: str = "/") -> float:
    """Retorna o percentual de uso do disco.

    Args:
        caminho: ponto de montagem a verificar.

    Returns:
        Percentual de uso (0 a 100).
    """
    uso = shutil.disk_usage(caminho)
    return uso.used / uso.total * 100


def enviar_telegram(texto: str) -> bool:
    """Envia mensagem via bot do Telegram."""
    try:
        resposta = requests.post(
            f"https://api.telegram.org/bot{TOKEN}/sendMessage",
            json={"chat_id": CHAT_ID, "text": texto, "parse_mode": "Markdown"},
            timeout=10,
        )
        return resposta.status_code == 200
    except requests.exceptions.RequestException as erro:
        logger.error("Falha ao enviar Telegram: %s", erro)
        return False


def main() -> None:
    """Executa a verificação e envia o alerta se necessário."""
    percentual = uso_disco()
    logger.info("Disco: %.1f%% usado", percentual)

    mensagem = f"💾 *Disco da VPS:* {percentual:.1f}% usado"
    if percentual >= LIMITE_PERCENTUAL:
        mensagem += f"\n\n⚠️ *ATENÇÃO:* limite de {LIMITE_PERCENTUAL:.0f}% excedido!"

    if enviar_telegram(mensagem):
        logger.info("Mensagem enviada")
    else:
        logger.error("Mensagem NÃO enviada")


if __name__ == "__main__":
    main()

💡 Resultado: código pronto para produção — tipado, documentado, com logging e modular.





HB Estudos · ← Voltar ao índice de Python