Convenciones y Guía de Estilo
Modismos de Python acordados para el equipo - formato, nombres, tipado, errores y commits - aplicados por ruff en CI y documentados aquí para que las revisiones se centren en el diseño, no en el gusto.
Receta
from __future__ import annotations
from pathlib import Path
DATA_DIR = Path(__file__).resolve().parent / "data"
def load_config(path: Path) -> dict[str, str]:
try:
text = path.read_text(encoding="utf-8")
except OSError as exc:
raise ConfigError(f"no se puede leer {path}") from exc
return parse_env(text)Cuándo usar esto:
- Al escribir nuevos módulos en servicios compartidos
- Al revisar PRs para consistencia
- Al incorporar desarrolladores de otros lenguajes
- Al resolver debates de "¿qué patrón usamos?"
Ejemplo de Trabajo
# modules/billing/services/tax.py
from decimal import Decimal
from billing.domain.models import LineItem
from billing.settings import Settings
class TaxService:
def __init__(self, settings: Settings) -> None:
self._rate = Decimal(settings.vat_rate)
def compute(self, items: list[LineItem]) -> Decimal:
subtotal = sum((i.unit_price * i.quantity for i in items), start=Decimal("0"))
return (subtotal * self._rate).quantize(Decimal("0.01"))Mensaje de commit:
feat(billing): aplicar IVA a los artículos de línea de la UE [BILL-91]
Usa Decimal para cálculos monetarios. Añade una prueba de regresión para líneas con cantidad cero.Lo que esto demuestra:
from __future__ import annotationspara referencias futurasDecimalpara moneda - no float- Atributo privado
_rate; métodos públicos tipados - ID de ticket en el asunto del commit
Análisis Profundo
Cómo Funciona
- ruff - Reemplaza flake8, isort, pyupgrade; configuración en
pyproject.toml. - Longitud de línea - Típicamente 88 o 100 - el equipo elige una.
- Nombres - PEP 8: funciones
snake_case, clasesPascalCase, constantesSCREAMING_SNAKE. - Importaciones - Reglas de isort a través de ruff; sin
from module import *comodín.
Valores Predeterminados del Equipo
| Tema | Convención |
|---|---|
| Cadenas | Se prefieren f-strings |
| Rutas | pathlib.Path no os.path.join |
| Excepciones | Tipos específicos; raise ... from exc |
| Logging | logging.getLogger(__name__) no print |
| Async | No IO bloqueante en rutas async def |
| Dinero | Decimal o centavos enteros |
Notas de Python
# Extracto de pyproject.toml
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"] # si el formateador maneja la longitud de líneaTrampas
- Versiones de black/ruff por desarrollador - CI falla mientras que PR local pasa. Solución:
uv syncfija las versiones de las herramientas. - Type ignores sin ticket - La deuda se acumula. Solución:
# type: ignore[arg-type] # BILL-99. - Literales float para dinero - Errores de
0.1 + 0.2. Solución: Decimal o centavos enteros. except:vacío - Enmascara KeyboardInterrupt. Solución: capturar mínimoException, preferiblemente tipos específicos.- Atributos de clase mutables - Errores de estado compartido. Solución: instancias de dataclass o atributos de instancia.
- Patrones de framework inconsistentes - Globales de Flask en app FastAPI. Solución: seguir el ADR de servicios.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Documento de reglas de python para toda la organización | Estándares entre equipos | Se necesitan anulaciones específicas del servicio |
| Ruff automatizado solamente | Equipo pequeño | La incorporación necesita ejemplos de texto |
| Guía de estilo de Python de Google | Referencia externa | Conflictos con la configuración de ruff |
| Sin guía de estilo | Nunca | Las revisiones discuten detalles triviales para siempre |
Preguntas Frecuentes
¿Formato Black o ruff?
ruff format cuando ruff sea adoptado - una sola herramienta; no ejecutar ambas.
¿Comillas simples o dobles?
El formateador de ruff elige consistentemente - no pelear manualmente en la revisión.
¿Type hint en todas las variables locales?
API pública y locales no obvias - no name = "ada" en funciones pequeñas.
¿Docstrings requeridos?
Sí, para módulos/clases/funciones públicas - estilo Google o numpy según README.
¿Longitud máxima de línea 88 vs 100?
Elige una en pyproject.toml - cumple con las expectativas del formateador y revisores.
¿Importaciones relativas vs absolutas?
Absolutas from billing.domain import models en aplicaciones - relativas solo dentro de paquetes pequeños según ADR.
¿Enums vs literales?
StrEnum o Literal para conjuntos pequeños y cerrados - documentar en la sección de type-hints.
¿Nombres de pruebas?
test_<comportamiento>_cuando_<condición> - salida de fallo legible.
¿Tipos de commit convencionales?
feat, fix, chore, docs, refactor, test - coincide con la automatización del changelog si la hay.
¿Anular convención?
ADR o comentario en línea con la razón - no una única vez en silencio en un solo PR.
Relacionado
- PEP 8 y Referencia de Estilo - referencia
- 50 Reglas de Python - lista de reglas
- Ruff y Formateadores - herramientas
- Guía de Revisión de Código - enfoque de revisión
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.