Ana quiere ver el catálogo de Papyrus ordenado por precio, saber cuál es el libro más barato y filtrar los que tienen stock. Todas esas operaciones tienen algo en común: una función principal (sorted(), min(), un filtro) necesita que le digas con qué criterio trabajar — y ese criterio suele ser una función diminuta, de una sola expresión, que no merece un def con nombre, docstring y hueco propio en el archivo. Para esos casos Python ofrece las funciones lambda: funciones anónimas de una línea que se escriben justo donde se usan. En esta lección aprenderás su sintaxis, cuándo son una buena idea y cuándo no (PEP 8 tiene opinión firme), y las pondrás a trabajar con sorted(), min()/max(), map() y filter() — comparando estas dos últimas con las comprensiones del módulo 2, que a menudo las superan.

Contenido

  1. Sintaxis: anatomía de una lambda
  2. Lambda frente a def: cuándo sí y cuándo no
  3. El parámetro key: ordenar con sorted()
  4. min() y max() con criterio
  5. map() y filter() frente a comprensiones
  6. Proyecto: el catálogo de Papyrus ordenado por precio

Sintaxis: anatomía de una lambda

Una lambda es una función sin nombre reducida a su mínima expresión:

lambda parametros: expresion

Comparémosla con su def equivalente:

def precio_con_iva(base):
    return round(base * 1.04, 2)

# La misma lógica como lambda:
lambda base: round(base * 1.04, 2)

Correspondencia pieza a pieza:

def lambda
def precio_con_iva(base): lambda base:
Nombre de la función No tiene (es anónima)
Cuerpo de varias líneas, con if, bucles, etc. Una única expresión
return explícito Implícito: la expresión ES el valor devuelto
Docstring No admite

Restricción clave: el cuerpo debe ser una expresión — algo que produce un valor. Caben operaciones, llamadas a funciones, f-strings y el ternario del módulo 2 ("agotado" if stock == 0 else "disponible"), pero no sentencias: nada de asignaciones, while, for como bucle ni varias líneas. Si necesitas eso, necesitas un def.

Una lambda puede tener varios parámetros (lambda titulo, precio: f"{titulo}: {precio} €") e incluso valores por defecto, aunque en la práctica la inmensa mayoría reciben uno o dos.

Lambda frente a def: cuándo sí y cuándo no

Puedes ejecutar una lambda directamente, e incluso guardarla en una variable:

print((lambda base: round(base * 1.04, 2))(12.50))   # 13.0 — legal, pero rebuscado

con_iva = lambda base: round(base * 1.04, 2)          # legal, pero PEP 8 dice NO

Esa segunda línea funciona... y PEP 8 la desaconseja explícitamente: si le vas a dar nombre, usa def. Los motivos son prácticos:

  • Con def con_iva(base): ..., los errores y las herramientas de depuración muestran el nombre con_iva; una lambda asignada aparece como el anónimo <lambda>, más difícil de rastrear.
  • def admite docstring, varias líneas y crece sin reescrituras.
  • No se gana nada: con_iva = lambda ... ocupa lo mismo que el def y comunica peor.

Entonces, ¿para qué sirven? Para pasarlas como argumento a otra función, escritas en el punto exacto donde se usan. Esa es su casa:

Situación Herramienta
Criterio de una línea para sorted/min/max/map/filter, usado una sola vez lambda
La misma lógica se usa en dos o más sitios def con nombre
El criterio necesita if/else de verdad, validación o varias líneas def
Quieres documentar qué hace def (docstring)
Asignarla a un nombre "para abreviar" Nunca: def

Que una función se pueda pasar como argumento no debería sorprenderte a estas alturas: en Python las funciones son valores, como los números o las cadenas. buscar_libro (sin paréntesis, lección 03-01) es un valor que nombra una función; una lambda es lo mismo, pero sin pasar por un nombre.

El parámetro key: ordenar con sorted()

sorted(secuencia) devuelve una lista nueva ordenada (la original no cambia):

print(sorted(precios))    # [9.95, 12.5, 15.9]
print(sorted(catalogo))   # ['El Quijote', 'Hamlet', 'La Odisea'] (orden alfabético)

Pero Ana no quiere ordenar precios sueltos ni títulos: quiere ordenar libros por precio. Primero emparejamos título y precio con zip() (lección anterior) y luego entra el argumento keyword key: una función que, aplicada a cada elemento, produce el valor por el que ordenar.

libros = list(zip(catalogo, precios))
# [('La Odisea', 12.5), ('Hamlet', 9.95), ('El Quijote', 15.9)]

por_precio = sorted(libros, key=lambda libro: libro[1])
# [('Hamlet', 9.95), ('La Odisea', 12.5), ('El Quijote', 15.9)]

Cómo funciona por dentro: para cada tupla libro, sorted() llama a la lambda, obtiene libro[1] (el precio) y ordena las tuplas comparando esos valores. La lambda no ordena nada; solo responde a la pregunta "¿por cuál de tus datos te comparo?".

Variantes útiles:

mas_caros = sorted(libros, key=lambda libro: libro[1], reverse=True)   # descendente
alfabetico = sorted(catalogo, key=lambda t: t.lower())                 # ignora mayúsculas

reverse=True invierte el orden — otro argumento keyword, como los de la lección 03-02. Y key=lambda t: t.lower() resuelve el clásico problema de que las mayúsculas alteren el orden alfabético.

min() y max() con criterio

min() y max() aceptan el mismo key, y responden preguntas de negocio en una línea:

mas_barato = min(libros, key=lambda libro: libro[1])
mas_caro = max(libros, key=lambda libro: libro[1])

print(f"Chollo del día: {mas_barato[0]} a {mas_barato[1]:.2f} €")
# Chollo del día: Hamlet a 9.95 €
print(f"Joya de la tienda: {mas_caro[0]} a {mas_caro[1]:.2f} €")
# Joya de la tienda: El Quijote a 15.90 €

Detalle importante: devuelven el elemento completo (la tupla título-precio), no el valor del criterio. Por eso mas_barato[0] da el título. Sin key, min(precios) daría 9.95 — el precio, pero sin saber de qué libro.

map() y filter() frente a comprensiones

Dos funciones clásicas que reciben otra función como argumento:

  • map(funcion, secuencia): aplica la función a cada elemento (transforma).
  • filter(funcion, secuencia): conserva los elementos para los que la función devuelve verdadero (criba).

Ambas son perezosas, como zip(): entregan resultados a medida que se piden, y list() los materializa.

# Transformar: precios de socio de todo el catálogo
list(map(lambda base: precio_final(base, socio=True), precios))
# [12.35, 9.83, 15.71]

# Cribar: precios por debajo de 13 €
list(filter(lambda base: base < 13, precios))
# [12.5, 9.95]

¿Te suenan estas operaciones? Son exactamente lo que hacías en el módulo 2 con comprensiones:

[precio_final(base, socio=True) for base in precios]   # map
[base for base in precios if base < 13]                # filter

Comparativa honesta:

map()/filter() + lambda Comprensión
Legibilidad con lógica propia Regular: la lambda añade ruido Alta: se lee como una frase
Con una función ya existente Muy buena: map(str.strip, lineas) Buena: [l.strip() for l in lineas]
Transformar y cribar a la vez Torpe: hay que anidar map(f, filter(g, x)) Natural: [f(x) for x in xs if g(x)]
Resultado Perezoso (hay que hacer list()) Lista directamente
Estilo dominante en Python moderno En retroceso Recomendado

Criterio para el curso: usa comprensiones por defecto; recurre a map()/filter() cuando la función ya exista y tenga nombre (ahí ni siquiera hace falta lambda). Aun así, debes saber leerlas: te las encontrarás constantemente en código ajeno.

Proyecto: el catálogo de Papyrus ordenado por precio

Juntemos todo en tarifa.py: la tabla del catálogo ordenada por precio, con los precios de socio de Luis y un pequeño informe:

IVA_LIBROS = 0.04
DESCUENTO_SOCIO = 0.05

catalogo = ["La Odisea", "Hamlet", "El Quijote"]
precios = [12.50, 9.95, 15.90]
stocks = [4, 6, 8]


def precio_final(base, socio=False, iva=IVA_LIBROS):
    """Precio final: descuento de socio (si procede) y luego IVA."""
    if socio:
        base = base * (1 - DESCUENTO_SOCIO)
    return round(base * (1 + iva), 2)


libros = list(zip(catalogo, precios, stocks))
por_precio = sorted(libros, key=lambda libro: libro[1])

print(f"{'Título':<12} {'Base':>7} {'Socio':>7}")
print("-" * 28)
for titulo, base, stock in por_precio:
    print(f"{titulo:<12} {base:>5.2f} € {precio_final(base, socio=True):>5.2f} €")

mas_barato = min(libros, key=lambda libro: libro[1])
print(f"\nRecomendación económica: {mas_barato[0]} ({mas_barato[1]:.2f} €)")
Título          Base   Socio
----------------------------
Hamlet          9.95 €  9.83 €
La Odisea      12.50 € 12.35 €
El Quijote     15.90 € 15.71 €

Recomendación económica: Hamlet (9.95 €)

Repasa el reparto de papeles: zip() empaqueta las listas paralelas en tuplas por libro, la lambda de key señala el precio como criterio, sorted() ordena sin tocar los datos originales y precio_final() — nuestra función con nombre, porque se reutiliza — calcula la columna de socios. Cada herramienta en su sitio.

Errores Comunes y Consejos

  • Asignar lambdas a nombres (f = lambda x: ...): funciona, pero PEP 8 lo veta y las trazas de error mostrarán <lambda> en vez de un nombre útil. Usa def.
  • Intentar meter sentencias en una lambda (lambda x: y = x + 1 o varias líneas): SyntaxError. Una lambda es una expresión; si no cabe, es un def.
  • Pasar la lambda llamada en vez de la lambda: sorted(libros, key=libro[1]) falla — key espera una función, no un valor. La forma correcta es key=lambda libro: libro[1].
  • Olvidar list() con map()/filter(): print(map(...)) muestra <map object at 0x...>, no los datos. Materialízalo con list() o recórrelo con for.
  • Lambdas kilométricas: si tu lambda tiene un ternario dentro de otro o pasa de media línea, nadie la entenderá mañana. Extrae un def con nombre y docstring.
  • Creer que sorted() modifica la lista: devuelve una lista nueva. (Las listas tienen también un método .sort() que sí ordena en el sitio; lo veremos en el módulo 4.)
  • Consejo: escribe la lambda pensando en la frase "para cada elemento, compárame por ___". Lo que rellene el hueco es el cuerpo de tu lambda.

Ejercicios

Ejercicio 1: estantería ordenada por stock

Con libros = list(zip(catalogo, precios, stocks)) (incluye "Fausto" con 21.00 € y stock 0: constrúyelo con catalogo + ["Fausto"], etc.), imprime los libros ordenados de menos a más stock, una línea por libro con el formato <título>: N uds.. Después, usa max() con key para imprimir cuál es el libro con más existencias.

Ejercicio 2: map/filter contra comprensiones

Partiendo de precios = [12.50, 9.95, 15.90, 21.00]: (a) obtén con map() y una lambda la lista de precios de socio (usa precio_final(base, socio=True)); (b) obtén con filter() y una lambda los precios base superiores a 12 €; (c) reescribe ambas como comprensiones; (d) escribe en una sola comprensión "precios de socio de los libros con base superior a 12 €" y explica por qué con map+filter sería menos legible.

Ejercicio 3: ordenar títulos por longitud

Ana quiere las etiquetas de la estantería ordenadas por longitud del título (más cortos primero) y, a igual longitud, alfabéticamente. Pista: la lambda puede devolver una tupla (criterio1, criterio2) — Python compara tuplas elemento a elemento, como viste al comparar cadenas. Aplícalo a ["El Quijote", "Hamlet", "Fausto", "La Odisea"].

Soluciones

Ejercicio 1:

titulos = catalogo + ["Fausto"]
bases = precios + [21.00]
unidades = stocks + [0]
libros = list(zip(titulos, bases, unidades))

for titulo, base, stock in sorted(libros, key=lambda libro: libro[2]):
    print(f"{titulo}: {stock} uds.")
# Fausto: 0 uds. / La Odisea: 4 uds. / Hamlet: 6 uds. / El Quijote: 8 uds.

top = max(libros, key=lambda libro: libro[2])
print(f"Más existencias: {top[0]} ({top[2]} uds.)")   # El Quijote (8 uds.)

El stock es el elemento [2] de cada tupla; cambiar el criterio de orden es cambiar un índice en la lambda.

Ejercicio 2:

# (a) y (b)
socios = list(map(lambda base: precio_final(base, socio=True), precios))
# [12.35, 9.83, 15.71, 20.75]
caros = list(filter(lambda base: base > 12, precios))
# [12.5, 15.9, 21.0]

# (c) comprensiones equivalentes
socios = [precio_final(base, socio=True) for base in precios]
caros = [base for base in precios if base > 12]

# (d) transformar y cribar a la vez
socios_caros = [precio_final(base, socio=True) for base in precios if base > 12]
# [12.35, 15.71, 20.75]

Con map+filter la versión (d) sería list(map(lambda b: precio_final(b, socio=True), filter(lambda b: b > 12, precios))): dos lambdas, dos llamadas anidadas y se lee de dentro hacia fuera. La comprensión expresa lo mismo de izquierda a derecha, como una frase.

Ejercicio 3:

titulos = ["El Quijote", "Hamlet", "Fausto", "La Odisea"]
ordenados = sorted(titulos, key=lambda t: (len(t), t))
print(ordenados)   # ['Fausto', 'Hamlet', 'La Odisea', 'El Quijote']

La lambda devuelve (longitud, título): Python ordena primero por longitud y usa el título como desempate. "Fausto" y "Hamlet" empatan a 6 letras y el orden alfabético los resuelve; "La Odisea" (9 caracteres, el espacio cuenta) precede a "El Quijote" (10). Sin el segundo elemento de la tupla, el orden entre los empatados a 6 letras dependería de su posición original.

Conclusión

Las lambdas completan tu caja de herramientas de funciones: sintaxis lambda params: expresion, cuerpo de una sola expresión con return implícito, y un territorio bien delimitado — criterios de usar y tirar para sorted(), min(), max(), map() y filter(), nunca sustitutas de def para lógica con nombre (PEP 8 dixit). De paso, el catálogo de Papyrus ya se ordena por precio, por stock o por lo que diga una lambda de una línea, y sabes cuándo una comprensión expresa lo mismo con más claridad. Pero hay un problema creciente: precio_final() está copiada en tarifa.py, y buscar_libro() vive encerrada en menu.py. Escribimos funciones para reutilizar... y seguimos copiándolas de archivo en archivo. La próxima lección resuelve exactamente eso: módulos y paquetes — crearemos papyrus_utils.py, el primer módulo propio de la tienda, y cualquier script podrá importar sus funciones con una línea.

Curso de Programación en Python

Módulo 1: Introducción a Python

Módulo 2: Estructuras de Control

Módulo 3: Funciones y Módulos

Módulo 4: Estructuras de Datos

Módulo 5: Programación Orientada a Objetos

Módulo 6: Manejo de Archivos

Módulo 7: Manejo de Errores y Excepciones

Módulo 8: Temas Avanzados

Módulo 9: Pruebas y Depuración

Módulo 10: Desarrollo Web con Python

Módulo 11: Ciencia de Datos con Python

Módulo 12: Proyecto Final

© Copyright 2026. Todos los derechos reservados