Lambdas y comprehensions
Estas tres características tienen algo en común: te permiten expresar ideas que de otra forma tomarían varias líneas en una sola expresión legible. Bien usadas, hacen el código más corto y claro. Mal usadas, lo vuelven ilegible. Este capítulo cubre cuándo recurrir a cada una y cuándo detenerse.
Funciones lambda
Una lambda es una función sin nombre, de una sola expresión. La creas con la palabra clave lambda. Su verdadera utilidad es que puedes escribirla en línea, justo donde la necesitas, sin definir primero una función con nombre. Esto es lo que la hace útil con sorted().
double = lambda x: x * 2
double(5) # 10Eso es equivalente a:
def double(x):
return x * 2En la mayoría de los casos, usa def. Las lambdas tienen una ventaja real: puedes escribirlas en línea, justo donde las necesitas, sin nombrarlas. Esto es lo que las hace útiles con sorted(), map() y filter():
players = [("Sofía", 87), ("Mateo", 74), ("Camila", 92)]
sorted(players, key=lambda p: p[1]) # ordenar por puntaje (ascendente)
sorted(players, key=lambda p: p[1], reverse=True) # ordenar por puntaje (descendente)Sin una lambda, tendrías que definir una función con nombre solo para el argumento key=. La lambda mantiene la intención local y visible.
Las lambdas pueden tomar múltiples argumentos:
add = lambda a, b: a + b
add(3, 4) # 7Cuándo usar una lambda: solo cuando sea una expresión simple usada en un solo lugar. Si se vuelve compleja, o necesitas reutilizarla, escribe un def propio. Una lambda que abarca varios operadores o requiere condicionales suele ser una señal para cambiar a def.
Comprehensions de listas
La transformación más común en Python: tomar una secuencia, hacer algo con cada elemento y obtener una nueva lista. Una list comprehension hace esto en una sola línea legible: [expression for item in iterable]. También puedes añadir un filtro con if.
La forma larga:
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
squares.append(n ** 2)La list comprehension:
squares = [n ** 2 for n in numbers]La estructura siempre es la misma: [expression for item in iterable].
scores = [87, 42, 96, 55, 71]
scaled = [s * 1.1 for s in scores] # aplicar un bono del 10%
as_grades = [f"{s}/100" for s in scores] # formatear cada unoFiltrar con una condición
Añade una cláusula if para incluir solo los elementos que pasen una prueba. El resultado es una nueva lista con solo los elementos en los que la condición es True.
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = [n for n in numbers if n % 2 == 0] # [2, 4, 6, 8]
odds = [n for n in numbers if n % 2 != 0] # [1, 3, 5, 7]scores = [87, 42, 96, 55, 71, 38]
passing = [s for s in scores if s >= 60] # [87, 96, 71]
failing = [s for s in scores if s < 60] # [42, 55, 38]Comprehensions anidados
Puedes anidar comprehensions para aplanar una lista de listas en una sola lista. Léelo de izquierda a derecha: por cada fila, por cada elemento en esa fila, incluye el elemento.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]Léelo de izquierda a derecha: por cada fila en matrix, por cada item en row, incluye item.
Los comprehensions anidados pueden volverse confusos rápidamente. Si toma más de un momento analizarlo, escribe los bucles explícitamente.
Dict comprehensions
Los dict comprehensions construyen un diccionario en una sola expresión, usando la misma idea que las list comprehensions: {key: value for item in iterable}. Añade un filtro con if igual que en las list comprehensions.
names = ["sofía", "mateo", "camila"]
scores = [87, 74, 92]
score_map = {name: score for name, score in zip(names, scores)}
# {"sofía": 87, "mateo": 74, "camila": 92}Con un filtro:
passing = {name: score for name, score in score_map.items() if score >= 80}
# {"sofía": 87, "camila": 92}words = ["apple", "banana", "cherry"]
word_lens = {word: len(word) for word in words}
# {"apple": 5, "banana": 6, "cherry": 6}Set comprehensions
Los set comprehensions construyen un conjunto en una sola expresión, con llaves y sin dos puntos. Como el resultado es un conjunto, los duplicados se eliminan automáticamente.
words = ["apple", "banana", "cherry", "apple"]
unique = {w.lower() for w in words} # {"apple", "banana", "cherry"}Usa set comprehensions cuando quieras valores únicos y no te importe el orden.
Expresiones generadoras
Los generadores se parecen a las list comprehensions con paréntesis en lugar de corchetes. La diferencia clave: una list comprehension construye toda la lista en memoria de una vez. Un generador produce valores uno a la vez, solo cuando se necesitan. Para secuencias grandes, esto usa muchísima menos memoria.
squares_gen = (n ** 2 for n in range(1000000))total = sum(n ** 2 for n in range(1000000)) # sum() consume el generadorCuando pasas un generador directamente a una función como sum(), max(), min() o any(), puedes omitir los paréntesis adicionales:
total = sum(n ** 2 for n in range(1000)) # un par de paréntesis, no dosPara la mayor parte del código cotidiano, las list comprehensions están bien. Usa generadores cuando estés procesando grandes conjuntos de datos o datos en streaming donde mantener todo en memoria sería un desperdicio.
zip()
zip() empareja elementos de dos o más secuencias para que puedas recorrerlas en paralelo. Se detiene en la secuencia más corta. Es la forma limpia de evitar gestionar índices cuando dos listas se corresponden entre sí.
names = ["Sofía", "Mateo", "Camila"]
scores = [87, 74, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Sofía: 87
# Mateo: 74
# Camila: 92zip() se detiene en la secuencia más corta. Si tus secuencias pueden tener longitudes diferentes, usa itertools.zip_longest() con un valor de relleno.
Para convertir de vuelta una lista zippeada de pares en dos listas separadas, usa zip(*pairs):
pairs = [("Sofía", 87), ("Mateo", 74), ("Camila", 92)]
names, scores = zip(*pairs)
# names = ("Sofía", "Mateo", "Camila")
# scores = (87, 74, 92)¿Qué hace * aquí?
*pairs desempaqueta la lista en argumentos separados: zip(*pairs) se convierte en zip(("Sofía", 87), ("Mateo", 74), ("Camila", 92)). El operador * se cubre en el capítulo Funciones.
zip() también es la forma limpia de iterar varias secuencias en paralelo sin gestionar índices manualmente:
before = [10, 20, 30]
after = [15, 18, 35]
for b, a in zip(before, after):
change = a - b
print(f"{b} -> {a} ({'+' if change >= 0 else ''}{change})")map() y filter()
map() y filter() son herramientas de estilo funcional más antiguas que hacen lo mismo que los comprehensions. Las verás en código antiguo, así que vale la pena saber qué significan. Prefiere los comprehensions para código nuevo; son más legibles para la mayoría de los desarrolladores de Python.
numbers = [1, 2, 3, 4, 5]
list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]Prefiere los comprehensions; son más legibles para la mayoría de los desarrolladores de Python. Usa map() cuando ya tengas una función con nombre que existe:
strings = ["1", "2", "3"]
numbers = list(map(int, strings)) # [1, 2, 3] (más limpio que un comprehension aquí)En la práctica
Filtrar una lista de jugadores por puntajes aprobatorios, clasificar por puntaje con sorted y una lambda, luego imprimir con posiciones enumeradas:
players = [
{"name": "Sofía", "score": 87},
{"name": "Mateo", "score": 42},
{"name": "Camila", "score": 96},
{"name": "Diego", "score": 55},
]
passing = [p for p in players if p["score"] >= 60]
ranked = sorted(passing, key=lambda p: p["score"], reverse=True)
score_map = {p["name"]: p["score"] for p in ranked}
for i, (name, score) in enumerate(score_map.items(), start=1):
print(f"{i}. {name}: {score}")
