¿Dos ifs en una comprensión? Dale un nombre de función
Si tu list comprehension necesita un segundo if, necesita un nombre de función. Una comprensión es una gran herramienta para una transformación y, como máximo, un filtro simple. En el momento en que apilas dos condiciones — o una condición más un bucle anidado — en una sola línea entre corchetes, escribiste algo que el próximo lector tiene que descomprimir mentalmente. Mejor ponle nombre.
Por qué una línea deja de ser legible
Una comprensión empaqueta tres cosas en una expresión: qué produces, de dónde viene y qué conservas. Con un for y un if, el ojo la procesa en una sola pasada. Agrega un segundo if, otra cláusula for o un ternario dentro de la expresión de salida, y el lector ahora malabarea 4–5 piezas móviles sin ningún paso intermedio con nombre.
Las guías de estilo son directas al respecto. La guía de estilo de Python de Google (sección 2.7) permite comprensiones pero establece que "no se permiten múltiples cláusulas for ni expresiones de filtro — optimiza para legibilidad, no para concisión". El principio central de PEP 8 es el mismo: la legibilidad cuenta, y los one-liners comprimidos e ingeniosos se desaconsejan porque el código se lee mucho más de lo que se escribe. Incluso el tutorial oficial de Python, justo después de mostrar una comprensión anidada, te dice que "en el mundo real deberías preferir funciones integradas a sentencias de flujo complejas".
La solución: expandir en una función generadora con nombre
Toma una línea como esta:
active_admins = [u.email for u in users
if u.is_active if u.role == "admin"]
Expándela en una función generadora cuyo nombre declare la intención:
def active_admin_emails(users):
for user in users:
if not user.is_active:
continue
if user.role != "admin":
continue
yield user.email
active_admins = list(active_admin_emails(users))
Acaban de pasar tres cosas:
- La lógica recibió un nombre.
active_admin_emailsdocumenta la intención en cada punto de llamada — sin necesidad de comentarios. - Se volvió testeable. Puedes probar la función directamente con tres fixtures mínimos: un admin inactivo, un no-admin activo, un admin activo. La comprensión inline solo podía probarse a través de lo que la rodeaba.
- Siguió siendo perezosa. Una función generadora produce elementos de a uno, igual que la prima de la comprensión, la generator expression — así que no pierdes nada en memoria. Llama a
list()solo cuando de verdad necesites una lista.
Reglas prácticas
| Forma | Veredicto |
|---|---|
Un for, sin if | Comprensión — ideal |
Un for, un if simple | Comprensión — todavía bien |
Dos cláusulas if (o if + ternario) | Función generadora con nombre |
Dos cláusulas for (bucles anidados) | Función con nombre o bucles explícitos |
| Cualquier comprensión que tuviste que cortar en dos líneas | Función con nombre |
El disparador es mecánico a propósito: "segundo if → nombre de función" es una regla que nunca hay que discutir en el code review.
Por qué importa más con un agente de código
Los agentes de código imitan el código que los rodea. Si tu base de código está llena de comprensiones densas de múltiples cláusulas, el agente aprende que anidar lógica en una línea es el estilo de la casa — y va a generar con gusto monstruos de cuatro cláusulas que después tienes que revisar carácter por carácter. Siembra el repositorio con funciones generadoras con nombre y el agente copia ese patrón: unidades pequeñas, con nombre, testeables individualmente, que además puede modificar con seguridad, porque cada condición vive en su propia línea con su propio diff.
Notas para usuarios avanzados
yieldestá prohibido dentro de comprensiones de todos modos. Desde Python 3.8,yieldyyield fromson ilegales dentro de comprensiones y generator expressions — así que en cuanto la lógica necesita hacer yield, una función generadora con nombre no es solo más limpia: es la única opción.- Fusiona condiciones cuando son una sola idea.
if u.is_active and u.role == "admin"es un predicado, no dos cláusulas. Si el predicado combinado todavía se lee como un solo concepto, extráelo a una función (is_active_admin(u)) y conserva la comprensión. - Prefiere las funciones integradas a la astucia. El ejemplo de transposición del tutorial reemplaza una comprensión anidada con
list(zip(*matrix)). Antes de nombrar una función, revisa si una función integrada ya lo dice mejor.
Recursos
- Google Python Style Guide §2.7 — Comprehensions & Generator Expressions
- Tutorial de Python — List Comprehensions (y comprensiones anidadas)
- PEP 8 — Style Guide for Python Code
- Referencia del lenguaje Python — Generator expressions y yield
- PEP 202 — List Comprehensions
Mira el reel y luego profundiza. Yeda AI Tips es una serie de videos cortos sobre programación asistida por IA — un consejo concreto por minuto. Read in English