Comprehensions e iteradores

Python · Lección 3

L2 Derivación Fase F0 Libro McKinney cap. 3 Prereqs Funciones y scope

La idea

Una comprehension dice qué quieres, no cómo recorrerlo. Es el mismo salto mental que después harás en pandas cuando dejes de escribir bucles sobre filas y empieces a escribir operaciones sobre columnas — y en NumPy cuando vectorices.

La estructura siempre es la misma:

[  expresión   for  elemento  in  iterable   if  condición  ]
   qué devuelvo      qué recorro              qué filtro

El if es opcional. Se lee de izquierda a derecha, salvo la expresión del principio, que se aplica al final.

Las cuatro formas

Fíjate que el diccionario tiene tres claves, no cuatro: "costa" aparecía dos veces y la segunda sobrescribió a la primera.

Listas contra generadores: la diferencia es memoria

Por qué un generador no ocupa memoria

[x**2 for x in range(n)] construye la lista completa antes de devolverla: reserva memoria para los n resultados, todos a la vez.

(x**2 for x in range(n)) no calcula nada al crearse. Devuelve un objeto que recuerda dónde va en el recorrido.

Cada vez que pides el siguiente elemento — con next(), o implícitamente en un for — el generador calcula ese valor, lo entrega, y se congela hasta la próxima petición. Solo existe un valor a la vez.

Consecuencia: la memoria de un generador es O(1) sin importar n, y la de la lista es O(n). Pero el generador es de un solo uso: al terminar de recorrerlo queda agotado.

Cuándo usar cada uno: lista si vas a recorrerla más de una vez, indexarla o conocer su largo. Generador si solo vas a recorrerla una vez, sobre todo si es grande o infinita — leer un CSV de 10 GB línea por línea, por ejemplo.

Anidadas y el orden que confunde

El orden de los for es el mismo que en los bucles anidados equivalentes: el primero es el externo. Pero cuidado con esta otra forma, que es distinta:

TipCuándo NO usar una comprehension

Si necesitas dos niveles de anidamiento con condiciones, o la expresión no cabe cómoda en una línea, el bucle explícito se lee mejor. Una comprehension ilegible no es más “pythónica” — es peor código.

Herramientas que acompañan

any y all con un generador adentro se cortan apenas saben la respuesta: any para en el primer True. Con una lista tendrías que calcular todo primero.

Ejercicio 1 — Filtrar y transformar

De la lista de ventas, obtén una lista con el ticket promedio (venta ÷ transacciones) solo de las sucursales con más de 50 transacciones, redondeado a 2 decimales.

La expresión es round(d["venta"] / d["trx"], 2), el iterable es datos, y la condición filtra por d["trx"] > 50.

Ejercicio 2 — Dict comprehension

Construye un diccionario de sucursal a venta, solo para las que superan 2000.

Una dict comprehension va entre llaves y usa clave: valor como expresión: {d["sucursal"]: d["venta"] for d in datos if ...}.

Ejercicio 3 — Generador y cortocircuito

Usa any con un generador para saber si alguna sucursal tiene ticket promedio mayor a 35.

any(...) recibe un iterable de booleanos. Dentro de los paréntesis de any no hacen falta paréntesis extra para el generador: any(d["venta"] / d["trx"] > 35 for d in datos).

Reto

En proyectos/notebooks/F0-retos.ipynb:

  1. Escribe un generador lineas_largas(ruta, n) que lea un archivo y entregue solo las líneas de más de n caracteres, sin cargar el archivo entero en memoria. Pruébalo con un archivo de ejemplo.
  2. Toma una comprehension anidada de tres niveles y reescríbela como bucles explícitos. Decide cuál se lee mejor y anota por qué en 50-Errores o en tu nota del tema.
  3. Mide con timeit una comprehension contra el bucle equivalente con .append() para 1 000 000 de elementos. Explica la diferencia: ¿de dónde sale?

Para el Cerebro

Lee McKinney cap. 3 (comprehensions y generadores) con estas preguntas escritas antes:

  1. ¿Qué hace yield y en qué se diferencia de return?
  2. ¿Qué es itertools.chain y cuándo lo usarías?
  3. ¿Por qué range(1_000_000) no ocupa memoria proporcional a un millón?
¿Cuál es la estructura de una comprehension?::[expresión for elemento in iterable if condición]
¿Cuánta memoria usa un generador frente a una lista de n elementos?::O(1) contra O(n)
¿Qué pasa al recorrer un generador dos veces?::La segunda pasada viene vacía: es de un solo uso
¿Por qué any() con generador es más rápido que con lista?::Cortocircuita en el primer True sin evaluar el resto
¿Cuándo NO usar una comprehension?::Cuando tiene anidamiento profundo o no cabe legible en una línea
¿Qué hace zip()?::Recorre dos o más iterables en paralelo, deteniéndose en el más corto

Fuentes

Lo que esta página demuestra sola. Los ejercicios corren en tu navegador.

Lo que viene de los libros.

Índices verificados el 11-09-2026 contra el índice publicado de cada libro.

→ Siguiente: Archivos, errores y módulos