List comprehension vs generator expression en Python

Publicado el: 11/07/2026
Tempo de leitura: 5 minutos
Comparação entre list comprehension e generator expression em Python

Las list comprehensions y las generator expressions permiten transformar o filtrar datos con una sintaxis compacta. A primera vista parecen casi iguales: una utiliza corchetes y la otra paréntesis. Sin embargo, su comportamiento en memoria, el momento en que calculan los valores y la forma de reutilizarlas son diferentes. Elegir correctamente puede mejorar la claridad y evitar consumir memoria innecesaria.

Para entender esta comparación conviene dominar las listas en Python, los bucles for, las funciones y el uso de list comprehensions. También es útil conocer que un iterable puede producir valores sin almacenarlos todos al mismo tiempo.

Qué es una list comprehension

Una list comprehension crea una lista completa inmediatamente:

cuadrados = [numero ** 2 for numero in range(10)]
print(cuadrados)

El resultado contiene los diez valores en memoria. Esto es conveniente cuando necesitas acceder varias veces, conocer la longitud, usar índices, ordenar o modificar la colección.

Qué es una generator expression

Una generator expression utiliza paréntesis y produce un generador:

cuadrados = (numero ** 2 for numero in range(10))
print(cuadrados)
print(next(cuadrados))

El objeto no calcula todos los cuadrados al crearse. Cada valor se genera cuando se solicita mediante next(), un bucle o una función consumidora como sum().

Diferencia principal: cálculo inmediato o perezoso

La lista realiza el trabajo de una vez. El generador aplica evaluación perezosa: calcula únicamente lo necesario.

lista = [n * 2 for n in range(1_000_000)]
generador = (n * 2 for n in range(1_000_000))

La lista almacena un millón de referencias y valores. El generador conserva la lógica y su estado actual. Esto puede reducir mucho la memoria cuando procesas grandes rangos o archivos.

Medir el tamaño del objeto

import sys

lista = [n for n in range(100_000)]
generador = (n for n in range(100_000))

print(sys.getsizeof(lista))
print(sys.getsizeof(generador))

getsizeof() no siempre cuenta todos los objetos internos, pero ilustra la diferencia. El tamaño del generador permanece aproximadamente constante, mientras la lista crece con los elementos.

Cuándo una lista es mejor

Utiliza una list comprehension cuando necesitas:

  • recorrer los resultados más de una vez;
  • acceder por índice;
  • usar len() inmediatamente;
  • ordenar o modificar la colección;
  • trabajar con un volumen pequeño o moderado.
precios_con_impuesto = [precio * 1.21 for precio in precios]
print(len(precios_con_impuesto))
print(precios_con_impuesto[0])
precios_con_impuesto.sort()

En este caso, la lista es clara y práctica.

Cuándo un generador es mejor

Utiliza una generator expression cuando:

  • los datos son muy grandes;
  • solo necesitas una pasada;
  • la función consumidora acepta iterables;
  • quieres iniciar el procesamiento sin esperar a calcular todo;
  • la fuente puede ser infinita.
total = sum(numero ** 2 for numero in range(1_000_000))

No es necesario crear una lista intermedia para sumar los valores.

Un generador se consume

generador = (n * 2 for n in range(5))

print(list(generador))
print(list(generador))

La primera conversión produce los valores. La segunda devuelve una lista vacía porque el generador ya llegó al final. Si necesitas volver a recorrer, crea otro generador o utiliza una lista.

Filtrar datos

pares_lista = [n for n in range(20) if n % 2 == 0]
pares_generador = (n for n in range(20) if n % 2 == 0)

La condición funciona igual. La diferencia sigue siendo el almacenamiento y el momento de cálculo.

Procesar un archivo grande

from pathlib import Path

ruta = Path("servidor.log")

with ruta.open(encoding="utf-8") as archivo:
    errores = (
        linea.strip()
        for linea in archivo
        if "ERROR" in linea
    )

    for error in errores:
        procesar(error)

El archivo se recorre línea por línea. No se carga todo en memoria. Este patrón es más adecuado que una lista cuando el registro ocupa varios gigabytes.

Encadenar transformaciones

numeros = range(1_000_000)
pares = (n for n in numeros if n % 2 == 0)
cuadrados = (n * n for n in pares)
limitados = (n for n in cuadrados if n < 10_000)

for valor in limitados:
    print(valor)

Cada etapa recibe valores de la anterior bajo demanda. Este pipeline evita colecciones intermedias, aunque demasiadas expresiones anidadas pueden dificultar la lectura.

El problema de los errores tardíos

Una lista ejecuta la transformación al crearla, por lo que un error aparece inmediatamente:

lista = [10 / n for n in [5, 2, 0, 1]]

El generador no falla hasta alcanzar el cero:

generador = (10 / n for n in [5, 2, 0, 1])
print(next(generador))
print(next(generador))
print(next(generador))

Esto puede ser útil, pero también hace que un problema aparezca lejos del lugar donde se creó la expresión.

Rendimiento: no siempre el generador gana

Los generadores ahorran memoria, pero una lista puede ser más rápida cuando los datos son pequeños y se reutilizan varias veces. Crear nuevamente el generador o recalcular una transformación costosa puede consumir más tiempo.

Mide el caso real con timeit y controla el consumo de memoria. No elijas únicamente por una regla general.

Pasar expresiones a funciones

Muchas funciones aceptan cualquier iterable:

maximo = max(n * 3 for n in datos)
minimo = min(n * 3 for n in datos)
total = sum(n * 3 for n in datos)

Cuando una generator expression es el único argumento, puedes omitir un par de paréntesis adicionales:

total = sum(n * 3 for n in datos)

Convertir un generador en lista

generador = (n ** 2 for n in range(10))
resultado = list(generador)

Esto conserva la posibilidad de construir el pipeline de forma perezosa y materializar únicamente al final. Sin embargo, al llamar a list() vuelves a almacenar todos los elementos.

Generadores infinitos

La biblioteca itertools permite fuentes infinitas. La documentación oficial de itertools describe funciones como count(), cycle() y repeat().

from itertools import count, islice

numeros = (n * 10 for n in count(1))
print(list(islice(numeros, 5)))

Una lista infinita es imposible, pero un generador puede producir valores indefinidamente. Siempre limita su consumo.

Semántica de las expresiones generadoras

La referencia oficial de expresiones generadoras detalla cómo se evalúan y cómo capturan el contexto. La primera parte iterable se evalúa al crear el generador, mientras las operaciones internas avanzan durante la iteración.

Evitar expresiones demasiado complejas

Una expresión compacta no siempre es más legible:

resultado = [transformar(x) for grupo in datos for x in grupo if validar(x)]

Si hay varias condiciones, manejo de errores o pasos con nombres útiles, utiliza un bucle o una función generadora:

def valores_validos(datos):
    for grupo in datos:
        for valor in grupo:
            if validar(valor):
                yield transformar(valor)

yield mantiene la evaluación perezosa y mejora la claridad.

Comparación práctica

# Lista: necesitamos reutilizar e indexar
usuarios_activos = [u for u in usuarios if u.activo]
mostrar(usuarios_activos)
exportar(usuarios_activos)

# Generador: una sola exportación de muchos registros
usuarios_activos = (u for u in usuarios if u.activo)
exportar(usuarios_activos)

La decisión depende de cómo se consumirá el resultado, no únicamente del tamaño de la fuente.

Errores frecuentes

Los problemas más comunes son intentar usar len() o índices en un generador, recorrerlo dos veces, crear una lista enorme solo para pasarla a sum(), usar generadores cuando se necesitan varias pasadas y escribir expresiones demasiado complejas. También debes recordar que un generador puede mantener referencias a recursos mientras no se consume completamente.

Conclusión

Una list comprehension calcula y almacena todos los resultados, mientras una generator expression produce valores bajo demanda. Las listas son mejores para reutilización, índices y colecciones pequeñas. Los generadores destacan en una sola pasada, archivos grandes, pipelines y fuentes potencialmente infinitas. La elección correcta combina claridad, memoria y patrón de uso. Cuando la expresión deja de ser legible, una función con yield suele ser la mejor alternativa.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Pattern matching com match case em Python
    Fundamentos
    Foto de perfil de Leandro Hirt da Academify

    match-case en Python: guía de pattern matching

    Aprende match-case en Python: patrones con valores, listas, diccionarios, dataclasses, guards, capturas, constantes y ejemplos prácticos.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Uso de map e filter para agilizar código Python
    Fundamentos
    Foto de perfil de Leandro Hirt da Academify

    map() y filter() en Python: guía práctica y eficiente

    Aprende map() y filter() en Python: transformación, filtrado, evaluación perezosa, pipelines, lambdas, errores y comparación con comprehensions.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Logo do Python com as palavras global e local
    Fundamentos
    Foto de perfil de Leandro Hirt da Academify

    Alcance de variables en Python: entiende la regla LEGB

    Entiende el alcance de variables en Python con la regla LEGB, global, nonlocal, closures, sombreado, mutabilidad y UnboundLocalError.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Operações matemáticas usando o módulo math em Python
    Fundamentos
    Foto de perfil de Leandro Hirt da Academify

    Módulo math en Python: operaciones matemáticas esenciales

    Aprende el módulo math en Python: raíces, potencias, redondeos, trigonometría, logaritmos, constantes, isclose y ejemplos prácticos.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Geração de números aleatórios seguros usando secrets em Python
    Fundamentos
    Foto de perfil de Leandro Hirt da Academify

    Módulo secrets en Python: números y tokens seguros

    Aprende secrets en Python para generar contraseñas, códigos y tokens seguros con randbelow, choice, token_hex, token_urlsafe y compare_digest.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Uso do operador walrus para atribuições inline em Python
    Fundamentos
    Foto de perfil de Leandro Hirt da Academify

    Operador walrus en Python: guía con ejemplos

    Aprende el operador walrus := en Python: condiciones, while, comprensiones, lectura por bloques, regex, alcance y buenas prácticas.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026