Una list comprehension es una forma de construir una lista en Python sin escribir un bucle for completo. La idea es simple: en vez de declarar una lista vacía, iterar y hacer append en cada paso, lo encapsulas todo en una sola expresión.
La sintaxis básica tiene tres partes:
[expresión for elemento in iterable]
Compara esto con el equivalente usando un bucle for:
# Con bucle for
cuadrados = []
for n in range(10):
cuadrados.append(n ** 2)
# Con list comprehension
cuadrados = [n ** 2 for n in range(10)]
El resultado es el mismo. La diferencia es que la segunda versión es más compacta y, una vez que te acostumbras a leerla, también más clara.
¿Cuándo conviene el bucle for clásico? Cuando la lógica es compleja, cuando tienes varios niveles de anidamiento o cuando necesitas hacer varias cosas dentro del bucle. Si meter todo en una línea la hace ilegible, usa el for. La legibilidad no es negociable.
Tres pasos para leer cualquier list comprehension
La gente que empieza con Python a veces se atasca ante una list comprehension porque intenta leerla de izquierda a derecha como si fuera una frase normal. No funciona así. Hay un orden mental para descomponerla.
Paso 1: identifica la expresión
Es lo que está a la izquierda del for. Es lo que se produce en cada iteración, el valor que va a parar a la lista.
Paso 2: identifica el iterable
Es lo que viene después del in. De ahí sale cada elemento.
Paso 3: identifica la condición
Si hay un if al final, es un filtro. Solo los elementos que cumplan la condición pasan a la lista.
Apliquémoslo a un ejemplo concreto:
resultado = [palabra.upper() for palabra in texto.split() if len(palabra) > 3]
- Expresión:
palabra.upper() cada palabra se convierte a mayúsculas - Iterable:
texto.split() la fuente son las palabras del texto - Condición:
if len(palabra) > 3 solo las palabras de más de 3 caracteres
Una vez que tienes ese esquema en la cabeza, cualquier list comprehension se vuelve legible en segundos.
Filtrar con condición: [x for x in lista if condición]
El filtrado es uno de los usos más frecuentes. La condición va al final, no al principio, y ese es el error que más veces cometen los que vienen de otros lenguajes.
# Filtrar números pares
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pares = [n for n in numeros if n % 2 == 0]
# [2, 4, 6, 8, 10]
# Filtrar palabras por longitud
palabras = ['sol', 'python', 'mar', 'programar', 'ir']
largas = [p for p in palabras if len(p) > 4]
# ['python', 'programar']
La condición if actúa como un portero: solo deja pasar los elementos que devuelven True. No hay else aquí (si necesitas un else, vas a la siguiente sección).
Transformar y filtrar a la vez
Puedes combinar la transformación y el filtrado en la misma expresión. La condición sigue al final, y la expresión de la izquierda trabaja solo con los elementos que han pasado el filtro:
# Doblar solo los positivos
numeros = [-3, -1, 0, 2, 4, 7]
dobles_positivos = [x * 2 for x in numeros if x > 0]
# [4, 8, 14]
Un ejemplo más real: tienes una lista de emails y quieres extraer solo los dominios de las cuentas de Gmail.
emails = [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]'
]
dominios_gmail = [email.split('@')[1] for email in emails if 'gmail' in email]
# ['gmail.com', 'gmail.com']
Si también necesitas transformar los que no cumplen la condición (un if/else ternario), la sintaxis cambia un poco: el if/else va en la expresión, antes del for:
# 'par' o 'impar' para cada número
etiquetas = ['par' if n % 2 == 0 else 'impar' for n in range(5)]
# ['par', 'impar', 'par', 'impar', 'par']
Ojo: esto puede volverse confuso rápido. Si la expresión ternaria es larga, mejor un bucle for.
List comprehensions con strings
Los strings en Python son iterables, así que puedes recorrer sus caracteres directamente:
# Mayúsculas de cada carácter
cadena = 'python'
letras = [c.upper() for c in cadena]
# ['P', 'Y', 'T', 'H', 'O', 'N']
# Filtrar solo vocales
vocales = [c for c in cadena if c in 'aeiou']
# ['y', 'o'] (ojo, 'y' no es vocal en español, pero en este caso sí está en la cadena)
También puedes construir listas de palabras desde un texto, lo que viene bien para preprocesar datos antes de análisis:
texto = "Python es un lenguaje muy versátil y fácil de aprender"
palabras_largas = [p for p in texto.split() if len(p) >= 5]
# ['Python', 'lenguaje', 'versátil', 'aprender']
Esto conecta bien con tareas de automatización de tareas con Python donde necesitas limpiar o filtrar texto de forma rápida antes de procesarlo.
Comprehensions anidadas (con precaución)
Puedes anidar comprehensions para trabajar con estructuras de datos en dos dimensiones, como matrices. El caso más limpio es crear una matriz de ceros:
filas = 3
cols = 4
matriz = [[0] * cols for _ in range(filas)]
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Nota: aquí el _ indica que la variable de iteración no se usa. Es una convención habitual en Python.
También puedes aplanar una lista de listas con una comprehension anidada:
matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
plana = [n for fila in matriz for n in fila]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
El orden de los for aquí sigue el mismo orden que tendrían si los escribieras anidados:
# Equivalente con bucles for
plana = []
for fila in matriz:
for n in fila:
plana.append(n)
Hasta aquí todo bien. El problema viene cuando empiezas a meter condiciones en comprehensions anidadas. Si tienes que explicarte a ti mismo lo que hace la línea, es señal de que toca parar y usar bucles normales. La regla práctica: más de dos for en una comprehension es casi siempre demasiado.
Dict comprehensions y set comprehensions
La misma idea funciona para diccionarios y conjuntos, con sintaxis ligeramente distinta.
Dict comprehension
# De una lista de pares (clave, valor)
pares = [('nombre', 'Ana'), ('edad', 30), ('ciudad', 'Madrid')]
diccionario = {k: v for k, v in pares}
# {'nombre': 'Ana', 'edad': 30, 'ciudad': 'Madrid'}
# Invertir un diccionario
original = {'a': 1, 'b': 2, 'c': 3}
invertido = {v: k for k, v in original.items()}
# {1: 'a', 2: 'b', 3: 'c'}
Set comprehension
# Eliminar duplicados y quedarte con los pares
numeros = [1, 2, 2, 3, 4, 4, 5]
pares_unicos = {n for n in numeros if n % 2 == 0}
# {2, 4} sin orden garantizado, sin duplicados
La diferencia visual: las llaves {} en vez de corchetes [], y en el caso del diccionario la expresión tiene la forma clave: valor.
Este tipo de construcciones es muy útil cuando trabajas con Python para computación científica, donde transformar y filtrar datos de forma eficiente marca la diferencia.
Rendimiento frente a legibilidad
Las list comprehensions son algo más rápidas que el bucle for equivalente porque Python las ejecuta en una sola operación optimizada internamente. La diferencia es real pero pequeña, y en la mayoría de proyectos no va a ser el cuello de botella.
import timeit
# Medir bucle for vs list comprehension
tiempo_for = timeit.timeit(
stmt="resultado = []nfor n in range(1000):n resultado.append(n**2)",
number=10000
)
tiempo_comp = timeit.timeit(
stmt="resultado = [n**2 for n in range(1000)]",
number=10000
)
print(f"For: {tiempo_for:.3f}s")
print(f"Comprehension: {tiempo_comp:.3f}s")
En la práctica, la comprehension suele salir un 20-40% más rápida. Pero si tienes que torcer la sintaxis para que quepa en una línea, pierdes más tiempo leyendo ese código después de lo que ganas en velocidad de ejecución.
La regla que funciona bien: si la comprehension cabe en una línea de menos de 80 caracteres y se entiende a la primera, úsala. Si no, usa un bucle for. No hay premio por el código más compacto.
Resumen de sintaxis
- Lista básica:
[expresión for x in iterable] - Con filtro:
[expresión for x in iterable if condición] - Con if/else:
[a if cond else b for x in iterable] - Anidada:
[expr for fila in matriz for x in fila] - Dict:
{k: v for k, v in pares} - Set:
{expr for x in iterable}
Con eso tienes todo lo que necesitas para leer cualquier comprehension que te encuentres y para decidir cuándo escribir la tuya.
Imagen: Pexels / Myburgh Roux
