EAFP vs LBYL
Python favorece EAFP (Easier to Ask Forgiveness than Permission - Más fácil pedir perdón que permiso): intenta la operación y maneja el fallo con excepciones. LBYL (Look Before You Leap - Mira antes de saltar) comprueba las condiciones primero. Ambas son válidas; EAFP es idiomático cuando la ruta feliz es común y los fallos son excepcionales.
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
# EAFP - intenta, luego maneja
try:
value = mapping[key]
except KeyError:
value = default
# LBYL - guarda, luego actúa
if key in mapping:
value = mapping[key]
else:
value = defaultCuándo recurrir a esto:
- EAFP cuando la ruta de éxito domina y una búsqueda fallida es rara
- LBYL cuando el fallo es esperado y quieres evitar la sobrecarga de excepciones
- EAFP cuando comprobar todas las precondiciones es más difícil que intentarlo (tipado de pato)
- LBYL en bucles ajustados donde las excepciones se dispararían miles de veces por segundo
- EAFP cuando múltiples modos de fallo colapsan en un solo bloque
except
Ejemplo de Trabajo
Un analizador acepta entradas de dict o de objeto usando el tipado de pato EAFP, con LBYL solo para ramificación de tipos barata.
from typing import Any
def get_name(record: Any, default: str = "unknown") -> str:
"""Extrae un nombre de un registro similar a un dict o basado en atributos."""
if isinstance(record, dict):
return record.get("name", default)
try:
return record.name
except AttributeError:
return default
def parse_user_id(raw: str) -> int:
try:
return int(raw)
except ValueError as exc:
raise ValueError(f"id de usuario inválido: {raw!r}") from exc
samples = [
{"name": "Ada"},
type("User", (), {"name": "Grace"})(),
object(),
]
for sample in samples:
print(get_name(sample))
print(parse_user_id("42"))Lo que esto demuestra:
- La ruta del dict usa
.get()(guarda estilo LBYL a través de la API) - sin excepción para claves faltantes - El acceso a atributos usa EAFP - un
trycubre muchas formas de objetos - Volver a lanzar con
from excpreserva la cadena de traceback original - El tipado de pato evita cadenas
hasattrque compiten con atributos dinámicos
Inmersión Profunda
Cómo Funciona
- EAFP ejecuta la operación; Python lanza una excepción específica en caso de fallo
- LBYL evalúa predicados (
in,isinstance,hasattr) antes de la operación - El manejo de excepciones tiene un pequeño coste de configuración; en bucles calientes, millones de excepciones perjudican
try/exceptalrededor de la ruta de éxito mantiene el código lineal y legible- Múltiples cláusulas de guarda (LBYL) a menudo duplican el trabajo que la operación haría de todos modos
EAFP vs LBYL de un Vistazo
| Estilo | Fortaleza | Debilidad |
|---|---|---|
| EAFP | Ruta feliz lineal, maneja carreras | Coste de excepción si el fallo es común |
| LBYL | Predecible cuando el fallo es frecuente | Guardas verbosas, carreras TOCTOU con estado mutable |
Notas de Python
# Prefiere EAFP para la existencia de archivos si vas a abrirlos inmediatamente de todos modos
try:
with open(path) as f:
data = f.read()
except FileNotFoundError:
data = ""
# Prefiere LBYL cuando el fallo es la norma (escaneo de claves opcionales)
for key in optional_keys:
if key in cache:
process(cache[key])Trampas
- TOCTOU con LBYL - comprobar
key in dicty luegodict[key]puede fallar si otro hilo elimina la clave. Solución: usa EAFP (try/except KeyError) o.get(). except:desnudo - EAFP tienta a capturar todo. Solución: captura excepciones específicas (KeyError,ValueError,AttributeError).- Duplicación
hasattr+getattr-if hasattr(x, "foo"): x.foollama agetattrdos veces. Solución: usa EAFP contry/except AttributeError. - Flujo de control impulsado por excepciones en bucles - analizar cada línea con
int()en un bucle ajustado es lento cuando la mayoría de las líneas fallan. Solución: pre-filtra con regex o validación LBYL. - Ignorar excepciones - un
except: passvacío oculta errores. Solución: registra, vuelve a lanzar o devuelve un centinela con tipado explícito.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
.get() / setdefault() | Predeterminados de clave faltante en dict | Necesitas distinguir entre valor faltante y None |
Guardas isinstance | La ramificación de tipos es la decisión real | Solo necesitas comportamiento de tipado de pato |
match / coincidencia de patrones estructural | Formas de tipo complejas (3.10+) | Búsqueda de claves simple |
| Tipos opcionales + comprobaciones estáticas | Los contratos de API son fijos en tiempo de compilación | Se requiere tipado de pato en tiempo de ejecución |
Preguntas Frecuentes
¿Qué significa EAFP?
"Easier to Ask Forgiveness than Permission" (Más fácil pedir perdón que permiso) - intenta la operación y maneja excepciones en lugar de comprobar cada precondición primero.
¿Es EAFP siempre más Pythonic?
No. Usa EAFP cuando el éxito es común y las excepciones son raras. Usa LBYL cuando el fallo es esperado o las excepciones dominarían el rendimiento.
¿Cuándo debería usar dict.get() en lugar de try/except?
Cuando el contenedor es un dict y una clave faltante es un resultado normal. .get() es LBYL integrado en la API - sin sobrecarga de excepciones.
¿Funciona EAFP con código async?
Sí. try/except funciona alrededor de await de la misma manera que el código síncrono. Tampoco uses excepciones para el control de flujo en bucles async ajustados.
¿Cómo preservo los tracebacks al volver a lanzar?
Usa raise NewError("msg") from exc para encadenar excepciones, o raise desnudo dentro de except para volver a lanzar la original.
¿Es hasattr seguro antes de getattr?
Entre hasattr y el acceso, otra corrutina puede eliminar el atributo. Prefiere EAFP o getattr(obj, "name", default).
¿Qué pasa con las comprobaciones de permisos antes del acceso a archivos?
Para rutas sensibles a la seguridad, las comprobaciones explícitas pueden ser requeridas por política. Para I/O ordinario, EAFP con FileNotFoundError está bien.
¿Puede EAFP reemplazar a todos los if-statements?
No. Las ramas de lógica de negocio (if user.is_admin) no son casos de excepción. Reserva EAFP para fallos operativos.
¿Cómo se relaciona esto con el tipado de pato?
EAFP abraza el tipado de pato: llama al método que necesitas; maneja AttributeError si el objeto no lo soporta.
¿Debería capturar Exception o BaseException?
Captura tipos específicos primero. Nunca captures BaseException (incluye KeyboardInterrupt, SystemExit) en código de aplicación.
Relacionado
- Context Managers as a Pattern - EAFP para limpieza de recursos
- Null Object & Sentinels - evita valores faltantes ambiguos
- Pythonic Patterns Basics - resumen de la sección
- Common Anti-Patterns - cuando las guardas salen mal
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+.