L'Anna vol veure el catàleg de Papyrus ordenat per preu, saber quin és el llibre més barat i filtrar els que tenen estoc. Totes aquestes operacions tenen una cosa en comú: una funció principal (sorted(), min(), un filtre) necessita que li diguis amb quin criteri treballar — i aquest criteri sol ser una funció diminuta, d'una sola expressió, que no mereix un def amb nom, docstring i lloc propi al fitxer. Per a aquests casos Python ofereix les funcions lambda: funcions anònimes d'una línia que s'escriuen just on es fan servir. En aquesta lliçó n'aprendràs la sintaxi, quan són una bona idea i quan no (PEP 8 té opinió ferma), i les posaràs a treballar amb sorted(), min()/max(), map() i filter() — comparant aquestes dues últimes amb les comprensions del mòdul 2, que sovint les superen.

Contingut

  1. Sintaxi: anatomia d'una lambda
  2. Lambda davant de def: quan sí i quan no
  3. El paràmetre key: ordenar amb sorted()
  4. min() i max() amb criteri
  5. map() i filter() davant de comprensions
  6. Projecte: el catàleg de Papyrus ordenat per preu

Sintaxi: anatomia d'una lambda

Una lambda és una funció sense nom reduïda a la mínima expressió:

lambda parametres: expressio

Comparem-la amb el seu def equivalent:

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

# La mateixa logica com a lambda:
lambda base: round(base * 1.04, 2)

Correspondència peça a peça:

def lambda
def preu_amb_iva(base): lambda base:
Nom de la funció No en té (és anònima)
Cos de diverses línies, amb if, bucles, etc. Una única expressió
return explícit Implícit: l'expressió ÉS el valor retornat
Docstring No n'admet

Restricció clau: el cos ha de ser una expressió — una cosa que produeix un valor. Hi caben operacions, crides a funcions, f-strings i el ternari del mòdul 2 ("esgotat" if estoc == 0 else "disponible"), però no sentències: res d'assignacions, while, for com a bucle ni diverses línies. Si necessites això, necessites un def.

Una lambda pot tenir diversos paràmetres (lambda titol, preu: f"{titol}: {preu} €") i fins i tot valors per defecte, tot i que a la pràctica la immensa majoria en reben un o dos.

Lambda davant de def: quan sí i quan no

Pots executar una lambda directament, i fins i tot guardar-la en una variable:

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

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

Aquesta segona línia funciona... i PEP 8 la desaconsella explícitament: si li has de posar nom, fes servir def. Els motius són pràctics:

  • Amb def amb_iva(base): ..., els errors i les eines de depuració mostren el nom amb_iva; una lambda assignada apareix com l'anònim <lambda>, més difícil de rastrejar.
  • def admet docstring, diverses línies i creix sense reescriptures.
  • No s'hi guanya res: amb_iva = lambda ... ocupa el mateix que el def i comunica pitjor.

Llavors, per a què serveixen? Per passar-les com a argument a una altra funció, escrites al punt exacte on es fan servir. Aquesta és casa seva:

Situació Eina
Criteri d'una línia per a sorted/min/max/map/filter, usat una sola vegada lambda
La mateixa lògica es fa servir en dos o més llocs def amb nom
El criteri necessita if/else de debò, validació o diverses línies def
Vols documentar què fa def (docstring)
Assignar-la a un nom "per abreujar" Mai: def

Que una funció es pugui passar com a argument no t'hauria de sorprendre a aquestes altures: en Python les funcions són valors, com els nombres o les cadenes. cercar_llibre (sense parèntesis, lliçó 03-01) és un valor que anomena una funció; una lambda és el mateix, però sense passar per un nom.

El paràmetre key: ordenar amb sorted()

sorted(sequencia) retorna una llista nova ordenada (l'original no canvia):

print(sorted(preus))     # [9.95, 12.5, 15.9]
print(sorted(cataleg))   # ['El Quixot', 'Hamlet', "L'Odissea"] (ordre alfabetic)

Però l'Anna no vol ordenar preus solts ni títols: vol ordenar llibres per preu. Primer aparellem títol i preu amb zip() (lliçó anterior) i llavors entra l'argument keyword key: una funció que, aplicada a cada element, produeix el valor pel qual ordenar.

llibres = list(zip(cataleg, preus))
# [("L'Odissea", 12.5), ('Hamlet', 9.95), ('El Quixot', 15.9)]

per_preu = sorted(llibres, key=lambda llibre: llibre[1])
# [('Hamlet', 9.95), ("L'Odissea", 12.5), ('El Quixot', 15.9)]

Com funciona per dins: per a cada tupla llibre, sorted() crida la lambda, obté llibre[1] (el preu) i ordena les tuples comparant aquests valors. La lambda no ordena res; només respon a la pregunta "per quina de les teves dades et comparo?".

Variants útils:

mes_cars = sorted(llibres, key=lambda llibre: llibre[1], reverse=True)   # descendent
alfabetic = sorted(cataleg, key=lambda t: t.lower())                     # ignora majuscules

reverse=True inverteix l'ordre — un altre argument keyword, com els de la lliçó 03-02. I key=lambda t: t.lower() resol el clàssic problema que les majúscules alterin l'ordre alfabètic.

min() i max() amb criteri

min() i max() accepten el mateix key, i responen preguntes de negoci en una línia:

mes_barat = min(llibres, key=lambda llibre: llibre[1])
mes_car = max(llibres, key=lambda llibre: llibre[1])

print(f"Ganga del dia: {mes_barat[0]} a {mes_barat[1]:.2f} €")
# Ganga del dia: Hamlet a 9.95 €
print(f"Joia de la botiga: {mes_car[0]} a {mes_car[1]:.2f} €")
# Joia de la botiga: El Quixot a 15.90 €

Detall important: retornen l'element complet (la tupla títol-preu), no el valor del criteri. Per això mes_barat[0] dona el títol. Sense key, min(preus) donaria 9.95 — el preu, però sense saber de quin llibre.

map() i filter() davant de comprensions

Dues funcions clàssiques que reben una altra funció com a argument:

  • map(funcio, sequencia): aplica la funció a cada element (transforma).
  • filter(funcio, sequencia): conserva els elements per als quals la funció retorna cert (garbella).

Totes dues són peresoses, com zip(): lliuren resultats a mesura que es demanen, i list() els materialitza.

# Transformar: preus de soci de tot el cataleg
list(map(lambda base: preu_final(base, soci=True), preus))
# [12.35, 9.83, 15.71]

# Garbellar: preus per sota de 13 €
list(filter(lambda base: base < 13, preus))
# [12.5, 9.95]

Et sonen, aquestes operacions? Són exactament el que feies al mòdul 2 amb comprensions:

[preu_final(base, soci=True) for base in preus]   # map
[base for base in preus if base < 13]             # filter

Comparativa honesta:

map()/filter() + lambda Comprensió
Llegibilitat amb lògica pròpia Regular: la lambda afegeix soroll Alta: es llegeix com una frase
Amb una funció ja existent Molt bona: map(str.strip, linies) Bona: [l.strip() for l in linies]
Transformar i garbellar alhora Maldestre: cal imbricar map(f, filter(g, x)) Natural: [f(x) for x in xs if g(x)]
Resultat Peresós (cal fer list()) Llista directament
Estil dominant en el Python modern En retrocés Recomanat

Criteri per al curs: fes servir comprensions per defecte; recorre a map()/filter() quan la funció ja existeixi i tingui nom (allà ni tan sols cal lambda). Tot i així, has de saber llegir-les: te les trobaràs constantment en codi d'altri.

Projecte: el catàleg de Papyrus ordenat per preu

Ajuntem-ho tot a tarifa.py: la taula del catàleg ordenada per preu, amb els preus de soci d'en Lluís i un petit informe:

IVA_LLIBRES = 0.04
DESCOMPTE_SOCI = 0.05

cataleg = ["L'Odissea", "Hamlet", "El Quixot"]
preus = [12.50, 9.95, 15.90]
estocs = [4, 6, 8]


def preu_final(base, soci=False, iva=IVA_LLIBRES):
    """Preu final: descompte de soci (si escau) i despres l'IVA."""
    if soci:
        base = base * (1 - DESCOMPTE_SOCI)
    return round(base * (1 + iva), 2)


llibres = list(zip(cataleg, preus, estocs))
per_preu = sorted(llibres, key=lambda llibre: llibre[1])

print(f"{'Titol':<12} {'Base':>7} {'Soci':>7}")
print("-" * 28)
for titol, base, estoc in per_preu:
    print(f"{titol:<12} {base:>5.2f} € {preu_final(base, soci=True):>5.2f} €")

mes_barat = min(llibres, key=lambda llibre: llibre[1])
print(f"\nRecomanacio economica: {mes_barat[0]} ({mes_barat[1]:.2f} €)")
Titol           Base    Soci
----------------------------
Hamlet          9.95 €  9.83 €
L'Odissea      12.50 € 12.35 €
El Quixot      15.90 € 15.71 €

Recomanacio economica: Hamlet (9.95 €)

Repassa el repartiment de papers: zip() empaqueta les llistes paral·leles en tuples per llibre, la lambda de key assenyala el preu com a criteri, sorted() ordena sense tocar les dades originals i preu_final() — la nostra funció amb nom, perquè es reutilitza — calcula la columna de socis. Cada eina al seu lloc.

Errors Comuns i Consells

  • Assignar lambdes a noms (f = lambda x: ...): funciona, però PEP 8 ho veta i les traces d'error mostraran <lambda> en comptes d'un nom útil. Fes servir def.
  • Intentar posar sentències en una lambda (lambda x: y = x + 1 o diverses línies): SyntaxError. Una lambda és una expressió; si no hi cap, és un def.
  • Passar la lambda cridada en comptes de la lambda: sorted(llibres, key=llibre[1]) falla — key espera una funció, no un valor. La forma correcta és key=lambda llibre: llibre[1].
  • Oblidar list() amb map()/filter(): print(map(...)) mostra <map object at 0x...>, no les dades. Materialitza-ho amb list() o recorre-ho amb for.
  • Lambdes quilomètriques: si la teva lambda té un ternari dins d'un altre o passa de mitja línia, ningú no l'entendrà demà. Extreu un def amb nom i docstring.
  • Creure que sorted() modifica la llista: retorna una llista nova. (Les llistes tenen també un mètode .sort() que sí que ordena in situ; el veurem al mòdul 4.)
  • Consell: escriu la lambda pensant en la frase "per a cada element, compara'm per ___". El que ompli el buit és el cos de la teva lambda.

Exercicis

Exercici 1: prestatgeria ordenada per estoc

Amb llibres = list(zip(cataleg, preus, estocs)) (inclou-hi "Faust" amb 21.00 € i estoc 0: construeix-ho amb cataleg + ["Faust"], etc.), imprimeix els llibres ordenats de menys a més estoc, una línia per llibre amb el format <titol>: N unitats. Després, fes servir max() amb key per imprimir quin és el llibre amb més existències.

Exercici 2: map/filter contra comprensions

Partint de preus = [12.50, 9.95, 15.90, 21.00]: (a) obtén amb map() i una lambda la llista de preus de soci (fes servir preu_final(base, soci=True)); (b) obtén amb filter() i una lambda els preus base superiors a 12 €; (c) reescriu totes dues com a comprensions; (d) escriu en una sola comprensió "preus de soci dels llibres amb base superior a 12 €" i explica per què amb map+filter seria menys llegible.

Exercici 3: ordenar títols per longitud

L'Anna vol les etiquetes de la prestatgeria ordenades per longitud del títol (els més curts primer) i, a igual longitud, alfabèticament. Pista: la lambda pot retornar una tupla (criteri1, criteri2) — Python compara tuples element a element, com vas veure en comparar cadenes. Aplica-ho a ["El Quixot", "Hamlet", "Faust", "L'Odissea"].

Solucions

Exercici 1:

titols = cataleg + ["Faust"]
bases = preus + [21.00]
unitats = estocs + [0]
llibres = list(zip(titols, bases, unitats))

for titol, base, estoc in sorted(llibres, key=lambda llibre: llibre[2]):
    print(f"{titol}: {estoc} unitats")
# Faust: 0 unitats / L'Odissea: 4 unitats / Hamlet: 6 unitats / El Quixot: 8 unitats

top = max(llibres, key=lambda llibre: llibre[2])
print(f"Mes existencies: {top[0]} ({top[2]} unitats)")   # El Quixot (8 unitats)

L'estoc és l'element [2] de cada tupla; canviar el criteri d'ordre és canviar un índex a la lambda.

Exercici 2:

# (a) i (b)
socis = list(map(lambda base: preu_final(base, soci=True), preus))
# [12.35, 9.83, 15.71, 20.75]
cars = list(filter(lambda base: base > 12, preus))
# [12.5, 15.9, 21.0]

# (c) comprensions equivalents
socis = [preu_final(base, soci=True) for base in preus]
cars = [base for base in preus if base > 12]

# (d) transformar i garbellar alhora
socis_cars = [preu_final(base, soci=True) for base in preus if base > 12]
# [12.35, 15.71, 20.75]

Amb map+filter la versió (d) seria list(map(lambda b: preu_final(b, soci=True), filter(lambda b: b > 12, preus))): dues lambdes, dues crides imbricades i es llegeix de dins cap a fora. La comprensió expressa el mateix d'esquerra a dreta, com una frase.

Exercici 3:

titols = ["El Quixot", "Hamlet", "Faust", "L'Odissea"]
ordenats = sorted(titols, key=lambda t: (len(t), t))
print(ordenats)   # ['Faust', 'Hamlet', 'El Quixot', "L'Odissea"]

La lambda retorna (longitud, títol): Python ordena primer per longitud i fa servir el títol com a desempat. "Faust" (5 lletres) i "Hamlet" (6) van al davant per longitud; "El Quixot" i "L'Odissea" empaten a 9 caràcters (l'espai i l'apòstrof compten) i l'ordre alfabètic els resol. Sense el segon element de la tupla, l'ordre entre els empatats a 9 caràcters dependria de la seva posició original.

Conclusió

Les lambdes completen la teva caixa d'eines de funcions: sintaxi lambda params: expressio, cos d'una sola expressió amb return implícit, i un territori ben delimitat — criteris d'usar i llençar per a sorted(), min(), max(), map() i filter(), mai substitutes de def per a lògica amb nom (PEP 8 dixit). De passada, el catàleg de Papyrus ja s'ordena per preu, per estoc o pel que digui una lambda d'una línia, i saps quan una comprensió expressa el mateix amb més claredat. Però hi ha un problema creixent: preu_final() està copiada a tarifa.py, i cercar_llibre() viu tancada a menu.py. Escrivim funcions per reutilitzar... i continuem copiant-les de fitxer en fitxer. La propera lliçó resol exactament això: mòduls i paquets — crearem papyrus_utils.py, el primer mòdul propi de la botiga, i qualsevol script podrà importar-ne les funcions amb una línia.

Curs de Programació en Python

Mòdul 1: Introducció a Python

Mòdul 2: Estructures de Control

Mòdul 3: Funcions i Mòduls

Mòdul 4: Estructures de Dades

Mòdul 5: Programació Orientada a Objectes

Mòdul 6: Gestió de Fitxers

Mòdul 7: Gestió d'Errors i Excepcions

Mòdul 8: Temes Avançats

Mòdul 9: Proves i Depuració

Mòdul 10: Desenvolupament Web amb Python

Mòdul 11: Ciència de Dades amb Python

Mòdul 12: Projecte Final

© Copyright 2026. Tots els drets reservats