Depurar fragmentos de refactorización
Copie y pegue pares antes/después para los defectos cubiertos en esta sección. Cada fragmento es un patrón mínimo y ejecutable que puede insertar en una base de código con errores y adaptar.
Casos de uso
- Corregir argumentos predeterminados mutables en utilidades compartidas
- Romper importaciones circulares sin una proliferación de importaciones perezosas
- Detener el bloqueo del bucle de eventos asyncio en manejadores HTTP
- Reemplazar las matemáticas de dinero con punto flotante por Decimal o centavos enteros
- Reforzar las fusiones de pandas y el manejo de NaN
- Decodificar explícitamente bytes/str en los límites de E/S
- Estabilizar entornos con archivos de bloqueo uv
Implementación más sencilla
Comience con la corrección de una línea o cinco líneas (centinela None, codificación explícita, math.isclose). Envíe el cambio más pequeño con una prueba, luego refactorice la estructura si el patrón se repite.
Variaciones
Variación: Predeterminado mutable a centinela
# ANTES
def add(item, items=[]):
items.append(item)
return items
# DESPUÉS
def add(item, items=None):
if items is None:
items = []
items.append(item)
return itemsVariación: default_factory de Dataclass
# ANTES
from dataclasses import dataclass
@dataclass
class Cart:
lines: list[str] = []
# DESPUÉS
from dataclasses import dataclass, field
@dataclass
class Cart:
lines: list[str] = field(default_factory=list)Variación: Módulo hoja de importación circular
# ANTES
# orders.py importa billing.py; billing.py importa orders.py
# DESPUÉS
# types.py contiene dataclasses compartidas
from dataclasses import dataclass
@dataclass(frozen=True)
class OrderId:
value: strVariación: Llamada bloqueante en ruta asíncrona
# ANTES
async def route():
import time
time.sleep(1)
# DESPUÉS
import asyncio
async def route():
await asyncio.sleep(1)Variación: HTTP síncrono en FastAPI asíncrono
# ANTES
async def fetch(url: str):
import urllib.request
return urllib.request.urlopen(url).read()
# DESPUÉS
import httpx
async def fetch(client: httpx.AsyncClient, url: str):
return (await client.get(url)).contentVariación: Dinero de punto flotante a Decimal
# ANTES
total = 19.99 * 3
# DESPUÉS
from decimal import Decimal, ROUND_HALF_UP
total = (Decimal("19.99") * 3).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)Variación: Igualdad de punto flotante
# ANTES
if result == expected_float:
# DESPUÉS
import math
if math.isclose(result, expected_float, rel_tol=1e-9):Variación: Codificación de archivo de texto
# ANTES
text = open(path).read()
# DESPUÉS
from pathlib import Path
text = Path(path).read_text(encoding="utf-8")Variación: Concatenación str + bytes
# ANTES
payload = "prefix" + b"bytes"
# DESPUÉS
payload = "prefix".encode("utf-8") + b"bytes"Variación: Guardia de fusión de pandas
# ANTES
merged = left.merge(right, on="id")
# DESPUÉS
merged = left.merge(right, on="id", validate="one_to_one")
assert len(merged) == len(left)Variación: NaN antes de la suma
# ANTES
total = df["amount"].sum()
# DESPUÉS
total = df.dropna(subset=["amount"])["amount"].sum()Variación: Reproducibilidad del entorno
# ANTES
pip install -r requirements.txt
# DESPUÉS
uv sync --frozen
uv run pytestImplementación compleja
Patrón listo para producción que combina barreras de seguridad para la ruta principal de un microservicio de facturación:
from __future__ import annotations
import asyncio
import logging
from dataclasses import dataclass, field
from decimal import Decimal, ROUND_HALF_UP
from typing import Protocol
import httpx
log = logging.getLogger(__name__)
@dataclass(frozen=True)
class Money:
cents: int
@staticmethod
def from_decimal(amount: str, qty: int) -> "Money":
total = (Decimal(amount) * qty).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
return Money(int(total * 100))
class TaxClient(Protocol):
async def rate_for(self, region: str) -> Decimal: ...
class HttpxTaxClient:
def __init__(self, client: httpx.AsyncClient) -> None:
self._client = client
async def rate_for(self, region: str) -> Decimal:
resp = await self._client.get(f"/tax/{region}", timeout=5.0)
resp.raise_for_status()
return Decimal(str(resp.json()["rate"]))
@dataclass
class QuoteService:
tax_client: TaxClient
_cache: dict[str, Decimal] = field(default_factory=dict)
async def quote(self, region: str, unit_price: str, qty: int) -> Money:
subtotal = Money.from_decimal(unit_price, qty)
rate = self._cache.get(region)
if rate is None:
rate = await self.tax_client.rate_for(region)
self._cache[region] = rate
tax = (Decimal(subtotal.cents) / 100 * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
return Money(subtotal.cents + int(tax * 100))
async def main() -> None:
async with httpx.AsyncClient(base_url="https://example.test") as client:
svc = QuoteService(HttpxTaxClient(client))
log.info("quote", extra={"amount": svc.quote("TX", "19.99", 2)})
if __name__ == "__main__":
asyncio.run(main())Aspectos clave:
- El dinero se almacena como centavos enteros, lo que evita por completo los puntos flotantes.
- La E/S HTTP asíncrona utiliza
httpxcon tiempo de espera en lugar de urllib bloqueante. - La caché utiliza
default_factory=dict, no un valor predeterminado mutable. - El
Decimalde impuestos se analiza a partir de una cadena JSON, no de un punto flotante. - El hook de registro estructurado está listo para la correlación de solicitudes.
Trampas comunes
- Copiar y pegar sin pruebas - el fragmento arregla otro síntoma. Solución: agregue una prueba de regresión por cada fragmento aplicado.
- Conversión asíncrona parcial - solo la ruta externa es asíncrona mientras la biblioteca se bloquea. Solución: empuje
to_threado un cliente asíncrono a la llamada de E/S real. - Decimal a partir de JSON de punto flotante -
Decimal(resp.json()["x"])cuando x es un punto flotante reintroduce el error. Solución: convierta a cadena en la API o use centavos enteros. - Caché limitada omitida - el diccionario
_cachecrece indefinidamente en el fragmento anterior. Solución: agregue LRU/TTL cuando las regiones no estén delimitadas. validate=one_to_onedemasiado estricto - las uniones legítimas uno a muchos fallan. Solución: elija la cardinalidad correcta después de perfilar las claves.
Preguntas frecuentes
¿Debo aplicar todos los fragmentos durante un refactor?
No. Una clase de defecto por PR con pruebas. Los refactors combinados ocultan qué corrección resolvió el problema.
¿Dónde viven estos fragmentos en un repositorio de servicio?
Prefiera corregir módulos de origen y capturar patrones en la documentación interna o en esta referencia. Evite un snippets.py en crecimiento importado en producción.
¿Puede ruff corregir automáticamente alguno de estos?
ruff 0.9+ marca algunos valores predeterminados mutables y declaraciones de depuración. Los cambios de codificación y Decimal requieren revisión humana.
¿Cómo verifico que un fragmento arregló la producción?
Implemente detrás de métricas: tasa de errores, latencia p95, RSS de memoria y totales de conciliación comercial para correcciones de datos.
¿Son seguros de ejecutar los bloques "antes"?
Los ejemplos mínimos son para ilustración. Los bloques "antes" con valores predeterminados mutables modifican el estado compartido si se llaman repetidamente en un solo proceso.
¿Cómo interactúan los fragmentos con los verificadores de tipos?
Después de las correcciones, agregue tipos (list[str] | None, Decimal) para que mypy/pyright eviten regresiones.
¿Qué fragmento corrige las importaciones circulares a largo plazo?
Módulo types o protocols de hoja, no importaciones perezosas permanentes. La variación de importación perezosa es solo para emergencias.
¿Cómo elijo centavos enteros frente a Decimal?
Centavos enteros para moneda de escala fija en alto rendimiento. Decimal cuando las reglas fiscales requieren precisión arbitraria antes de cuantificar.
¿El `validate=` en la fusión ralentiza los pipelines?
Despreciable en datos moderados; invaluable durante el desarrollo. Elimine solo después de que existan verificaciones automatizadas de unicidad de claves.
¿Puedo usarlos en vistas de Django?
Sí. Aplique patrones de codificación y Decimal en vistas/servicios; mantenga el mapeo de límites ORM explícito.
¿Cuál es el fragmento más rápido para "funciona en mi máquina"?
uv sync --frozen más python -c "import pkg; print(pkg.__file__)" antes de una depuración más profunda.
¿Cómo se relacionan los fragmentos con las Herramientas de depuración?
Use fragmentos después de que pdb/logging haya identificado la clase de defecto. Las herramientas encuentran; los fragmentos arreglan estructuralmente.
Relacionado
- Errores de argumento predeterminado mutable - narrativa completa sobre valores predeterminados compartidos
- Escenarios de importación circular - diagnóstico de ciclos
- Deadlocks de asyncio y bloqueo del bucle - bloqueos asíncronos
- Errores de punto flotante y numéricos - detalles numéricos
- Defectos de canalización de datos - guardias de pandas/polars
Versiones de la pila: Esta página se escribió 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+.