A la lliçó anterior, Papyrus va aprendre a decidir: si hi ha estoc, ven; si el client és soci, descompta. Però vam tancar amb un problema pendent: l'Anna té un catàleg sencer, i escriure un if per cada llibre no escala. La solució són els bucles, estructures que repeteixen un bloc de codi tantes vegades com calgui. En aquesta lliçó dominaràs els dos bucles de Python: for, per recórrer col·leccions i seqüències de nombres, i while, per repetir mentre es compleixi una condició. Amb ells recorrerem el catàleg complet de Papyrus i sumarem la caixa del dia en poques línies.

Contingut

  1. El bucle for i la funció range()
  2. range() amb inici, fi i pas
  3. Recórrer cadenes i llistes senzilles amb for
  4. El bucle while: repetir mentre es compleixi una condició
  5. Comptadors i acumuladors: la caixa del dia
  6. Bucles imbricats
  7. enumerate(): índex i valor alhora
  8. for o while? Quan usar cadascun

El bucle for i la funció range()

Un bucle for repeteix un bloc de codi una vegada per cada element d'una seqüència. La seva forma més bàsica usa range(), una funció que genera una seqüència de nombres enters:

for numero in range(5):
    print(f"Iteracio numero {numero}")

Sortida:

Iteracio numero 0
Iteracio numero 1
Iteracio numero 2
Iteracio numero 3
Iteracio numero 4

Desglossem la sintaxi peça a peça:

  • for i in són paraules reservades: es llegeixen "per a cada numero dins de range(5)".
  • numero és la variable de bucle: a cada volta (cada iteració) pren el valor següent de la seqüència. Pots anomenar-la com vulguis, seguint snake_case.
  • range(5) genera els nombres 0, 1, 2, 3, 4. Sí: comença a 0 i acaba just abans de 5. Genera exactament 5 nombres, però el 5 no hi és inclòs. Aquesta convenció (inici inclòs, fi exclòs) és universal en Python i convé interioritzar-la com més aviat millor.
  • Els dos punts i el bloc indentat funcionen igual que a l'if: tot el que està indentat es repeteix; el que va després, sense indentar, s'executa una sola vegada en acabar el bucle.

Un ús immediat a Papyrus: imprimir la línia separadora del tiquet sense copiar i enganxar.

NOM_BOTIGA = "Papyrus"

print(f"=== {NOM_BOTIGA} ===")
for _ in range(3):
    print("- Gracies per la compra -")

Quan el valor de la variable de bucle no s'usa (només volem repetir 3 vegades), la convenció en Python és anomenar-la _ (guió baix). És un senyal per a qui llegeix: "aquest valor no importa".

range() amb inici, fi i pas

range() accepta fins a tres arguments, igual que el trossejat de seqüències que veuràs més endavant:

Crida Genera Significat
range(5) 0, 1, 2, 3, 4 Només stop: des de 0 fins a stop−1
range(1, 6) 1, 2, 3, 4, 5 start i stop: des de start fins a stop−1
range(0, 10, 2) 0, 2, 4, 6, 8 start, stop i step: saltant de step en step
range(10, 0, -1) 10, 9, ..., 1 Pas negatiu: compta enrere

Exemple amb les tres formes, numerant coses de la llibreria:

# Numerar les prestatgeries de Papyrus, de la 1 a la 6
for prestatgeria in range(1, 7):
    print(f"Revisant prestatgeria {prestatgeria}")

# Els dies parells del mes, per a les comandes al distribuidor
for dia in range(2, 31, 2):
    print(f"Comanda programada el dia {dia}")

# Compte enrere per a l'obertura
for segons in range(3, 0, -1):
    print(f"Obertura en {segons}...")
print("Papyrus obre les portes!")

Detalls a vigilar:

  • range(1, 7) arriba fins al 6, no fins al 7 (fi exclòs, sempre).
  • Amb pas negatiu, start ha de ser més gran que stop, o el rang serà buit i el bucle no farà cap volta (sense error: simplement no passa res).
  • range(0) o range(5, 5) també són rangs buits vàlids.

Recórrer cadenes i llistes senzilles amb for

La veritable potència del for en Python és que no només recorre nombres: recorre qualsevol seqüència d'elements (el que anomenem un iterable). Dos casos que ja podem usar:

Recórrer una cadena

Una cadena és una seqüència de caràcters, així que es pot recórrer lletra a lletra:

NOM_BOTIGA = "Papyrus"

for lletra in NOM_BOTIGA:
    print(lletra, end=" ")

Sortida:

P a p y r u s

A cada iteració, lletra val un caràcter de la cadena, en ordre. (Recorda el paràmetre end de print() del mòdul 1: aquí substitueix el salt de línia per un espai.)

Recórrer una llista senzilla

Per recórrer el catàleg de Papyrus necessitem agrupar els títols en una sola variable. Per a això Python té les llistes: una col·lecció ordenada de valors, escrita entre claudàtors i separada per comes. Les estudiarem a fons al mòdul 4; de moment en tenim prou sabent crear-les i recórrer-les:

cataleg = ["L'Odissea", "Hamlet", "El Quixot"]

for titol in cataleg:
    print(f"- {titol}")

Sortida:

- L'Odissea
- Hamlet
- El Quixot

Llegeix aquest bucle en veu alta: "per a cada titol dins de cataleg, imprimeix-lo". El for de Python està dissenyat per sonar a llenguatge natural. A cada volta, titol apunta a l'element següent de la llista, del primer a l'últim.

I per descomptat, dins del bucle podem usar tot el que hem après a la lliçó anterior. Combinem dues llistes paral·leles (mateixa posició = mateix llibre) amb un condicional:

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

for i in range(3):
    titol = cataleg[i]
    estoc = estocs[i]
    disponibilitat = "disponible" if estoc > 0 else "ESGOTAT"
    print(f"{titol:<12} | {disponibilitat}")

Aquí cataleg[i] accedeix a l'element de la posició i de la llista (les posicions també comencen a 0, com range). Aquest patró d'"índex manual" funciona, però de seguida veurem amb enumerate() una forma més elegant.

El bucle while: repetir mentre es compleixi una condició

El for és ideal quan saps què has de recórrer o quantes vegades has de repetir. Però de vegades no ho saps per endavant: "repeteix mentre quedi estoc", "pregunta fins que l'usuari escrigui alguna cosa vàlida". Per a això hi ha while:

while condicio:
    # es repeteix MENTRE la condicio sigui True
    instruccions

El flux és: avaluar la condició → si és True, executar el bloc i tornar a avaluar → si és False, sortir del bucle.

flowchart TD
    A[Inici] --> B{condició?}
    B -- True --> C[Executar bloc]
    C --> B
    B -- False --> D[Continuar després del bucle]

Exemple a Papyrus: simular vendes de L'Odissea fins a exhaurir l'estoc.

titol = "L'Odissea"
estoc = 4

while estoc > 0:
    print(f"Venut un exemplar de '{titol}'. En queden {estoc - 1}.")
    estoc = estoc - 1

print(f"'{titol}' esgotat. Anna ha de reposar-ne.")

Sortida:

Venut un exemplar de 'L'Odissea'. En queden 3.
Venut un exemplar de 'L'Odissea'. En queden 2.
Venut un exemplar de 'L'Odissea'. En queden 1.
Venut un exemplar de 'L'Odissea'. En queden 0.
'L'Odissea' esgotat. Anna ha de reposar-ne.

La línia estoc = estoc - 1 és crucial: alguna cosa dins del bucle ha d'acostar la condició a False. Si l'oblides, l'estoc no baixa mai, la condició sempre és certa i tens un bucle infinit: el programa no acaba mai (si et passa, talla l'execució amb Ctrl+C a la terminal). És l'error clàssic del while.

Per cert, estoc = estoc - 1 és tan freqüent que Python ofereix una drecera: estoc -= 1 ("resta 1 a estoc"). Existeixen les equivalents +=, *=, /=, etc. A partir d'ara les usarem.

Un altre ús típic del while: repetir una pregunta fins a obtenir una resposta vàlida, una de les millores pendents de fitxa_llibre.py:

resposta = ""
while resposta != "s" and resposta != "n":
    resposta = input("El client es soci? (s/n): ")

es_soci = resposta == "s"
print(f"Soci: {es_soci}")

Mentre l'usuari no escrigui exactament s o n, la pregunta es repeteix. Fixa't en el truc d'inicialitzar resposta = "" abans del bucle perquè la primera avaluació de la condició sigui possible (i doni True).

Comptadors i acumuladors: la caixa del dia

Dos patrons apareixen una vegada i una altra amb bucles, i mereixen nom propi:

  • Comptador: una variable que comença a 0 i suma 1 cada vegada que passa alguna cosa. Respon a "quantes vegades...?".
  • Acumulador: una variable que comença a 0 (o a "" per a textos) i va sumant quantitats. Respon a "quant en total...?".

L'exemple perfecte és la caixa del dia de Papyrus: en tancar, l'Anna vol saber quant ha ingressat i quantes vendes ha fet.

NOM_BOTIGA = "Papyrus"
vendes_del_dia = [12.50, 9.95, 15.90, 12.50, 9.95]  # cobraments d'avui

total_caixa = 0.0    # acumulador: suma de cobraments
num_vendes = 0       # comptador: nombre d'operacions

for venda in vendes_del_dia:
    total_caixa += venda
    num_vendes += 1
    print(f"Venda {num_vendes}: {venda:.2f} € (acumulat: {total_caixa:.2f} €)")

print("-" * 40)
print(f"{NOM_BOTIGA} — tancament de caixa")
print(f"Vendes fetes:       {num_vendes}")
print(f"Total del dia:      {total_caixa:.2f} €")
print(f"Mitjana per tiquet: {total_caixa / num_vendes:.2f} €")

Sortida:

Venda 1: 12.50 € (acumulat: 12.50 €)
Venda 2: 9.95 € (acumulat: 22.45 €)
Venda 3: 15.90 € (acumulat: 38.35 €)
Venda 4: 12.50 € (acumulat: 50.85 €)
Venda 5: 9.95 € (acumulat: 60.80 €)
----------------------------------------
Papyrus — tancament de caixa
Vendes fetes:       5
Total del dia:      60.80 €
Mitjana per tiquet: 60.80 €...

(La mitjana real per tiquet és 12.16 €; comprova-ho en executar.)

L'anatomia del patró, que repetiràs centenars de vegades:

  1. Inicialitzar el comptador/acumulador abans del bucle (si l'inicialitzes dins, es posaria a zero a cada volta i no acumularia res).
  2. Actualitzar dins del bucle (+=).
  3. Usar el resultat després del bucle.

Els comptadors també es combinen amb condicionals: comptar només el que compleix alguna cosa.

estocs = [4, 6, 8, 0, 2]
titols_sota_minim = 0

for estoc in estocs:
    if estoc <= 3:
        titols_sota_minim += 1

print(f"Titols que cal reposar: {titols_sota_minim}")   # 2

Bucles imbricats

Igual que els if, els bucles poden imbricar-se: un bucle dins d'un altre. El bucle interior s'executa complet per cada volta de l'exterior.

L'Anna vol etiquetes per a tots els exemplars físics: una etiqueta per unitat en estoc de cada títol.

cataleg = ["L'Odissea", "Hamlet", "El Quixot"]
estocs = [3, 2, 2]   # estoc reduit per abreujar la sortida

for i in range(3):                      # bucle exterior: cada titol
    titol = cataleg[i]
    for unitat in range(1, estocs[i] + 1):   # bucle interior: cada exemplar
        print(f"Etiqueta: {titol} — exemplar {unitat}/{estocs[i]}")

Sortida:

Etiqueta: L'Odissea — exemplar 1/3
Etiqueta: L'Odissea — exemplar 2/3
Etiqueta: L'Odissea — exemplar 3/3
Etiqueta: Hamlet — exemplar 1/2
Etiqueta: Hamlet — exemplar 2/2
Etiqueta: El Quixot — exemplar 1/2
Etiqueta: El Quixot — exemplar 2/2

Compta les iteracions: l'exterior fa 3 voltes i l'interior 3+2+2 = 7 en total. Amb bucles imbricats el nombre d'execucions es multiplica ràpid: dos bucles de 1000 voltes són un milió d'execucions del bloc interior. Tingues-ho present quan treballis amb dades grans.

Un clàssic visual per fixar la idea, la taula de multiplicar:

for fila in range(1, 4):
    for columna in range(1, 4):
        print(f"{fila * columna:>3}", end="")
    print()   # salt de linia en acabar cada fila

Sortida:

  1  2  3
  2  4  6
  3  6  9

Observa el print() buit: està indentat al nivell del bucle exterior, així que s'executa una vegada per fila, no una vegada per cel·la. Amb bucles imbricats, el nivell d'indentació de cada línia determina "a quina volta pertany" — revisa-ho sempre dues vegades.

enumerate(): índex i valor alhora

Tornem al patró de l'índex manual que hem usat amb les llistes paral·leles. Quan recorres una llista i necessites també la posició de cada element, Python ofereix quelcom millor que range() + claudàtors: la funció enumerate(), que a cada volta lliura la parella índex-valor.

cataleg = ["L'Odissea", "Hamlet", "El Quixot"]

for posicio, titol in enumerate(cataleg, start=1):
    print(f"{posicio}. {titol}")

Sortida:

1. L'Odissea
2. Hamlet
3. El Quixot

Com funciona:

  • enumerate(cataleg) va generant parelles: (0, "L'Odissea"), (1, "Hamlet"), (2, "El Quixot").
  • Al for escrivim dues variables separades per coma (posicio, titol), i Python desempaqueta cada parella en elles — és la mateixa assignació múltiple que vas veure al mòdul 1.
  • L'argument opcional start=1 fa que la numeració comenci a 1, perfecte per a llistats de cara al públic (per defecte comença a 0).

Compara les dues versions del mateix llistat numerat:

# Amb index manual: funciona, pero fa mes soroll
for i in range(len(cataleg)):
    print(f"{i + 1}. {cataleg[i]}")

# Amb enumerate: mes clar i mes pythonic
for posicio, titol in enumerate(cataleg, start=1):
    print(f"{posicio}. {titol}")

(len() retorna la longitud d'una seqüència: len(cataleg) és 3. La usarem constantment.) Regla pràctica: si només necessites els valors, for x in llista; si també necessites la posició, enumerate(); reserva range(len(...)) per a casos molt concrets, com recórrer dues llistes paral·leles per índex.

for o while? Quan usar cadascun

Pregunta que et fas Bucle adequat Exemple a Papyrus
"Per a cada element d'aquesta col·lecció..." for Recórrer el catàleg
"Exactament N vegades" for amb range(N) Imprimir 3 línies de tiquet
"Mentre es compleixi aquesta condició..." while Vendre mentre quedi estoc
"Fins que l'usuari faci X" while Repetir la pregunta fins a resposta vàlida
"No sé quantes voltes caldran" while Esperar una entrada correcta

Regla d'or: si coneixes per endavant la col·lecció o el nombre de repeticions, usa for; si l'aturada depèn d'alguna cosa que passa durant l'execució, usa while. A la pràctica, el for és molt més freqüent en Python.

Errors Comuns i Consells

  • Esperar que range(5) inclogui el 5: genera del 0 al 4. Si vols de l'1 al 5, escriu range(1, 6). El clàssic "error d'un de més/de menys" (off-by-one) neix gairebé sempre aquí.
  • El bucle infinit al while: oblidar actualitzar la variable de la condició (estoc -= 1, llegir un nou input()...). Si el teu programa "es queda penjat", és això; interromp amb Ctrl+C i revisa què hauria de canviar a cada volta.
  • Inicialitzar l'acumulador dins del bucle: total = 0 ha d'anar abans del for; a dins, es reinicia a cada volta i el total final seria només l'última venda.
  • Modificar la variable de bucle d'un for esperant alterar el recorregut: fer numero += 10 dins de for numero in range(5) no salta iteracions; a la volta següent numero pren igualment el valor següent del rang. Per controlar els salts usa el pas de range() o un while.
  • Indentació en bucles imbricats: una línia indentada un nivell de més o de menys canvia quantes vegades s'executa. Si un print surt repetit més vegades del compte, comprova'n el nivell.
  • Consell: anomena la variable de bucle en singular i la col·lecció en plural (for titol in titols). El codi es llegeix sol.
  • Consell: abans d'escriure un bucle, digues en veu alta què vols: si la frase comença per "per a cada..." és un for; si comença per "mentre..." és un while. Funciona sorprenentment bé.

Exercicis

Exercici 1: catàleg alineat amb numeració

Reprèn la taula alineada de cataleg_inicial.py (mòdul 1) i genera-la ara amb un bucle. Amb les llistes titols = ["L'Odissea", "Hamlet", "El Quixot"] i preus = [12.50, 9.95, 15.90], usa enumerate() per imprimir:

1. L'Odissea    12.50 €
2. Hamlet        9.95 €
3. El Quixot    15.90 €

(Títol alineat a l'esquerra en 12 caràcters, preu a la dreta en 6 amb dos decimals.)

Exercici 2: caixa del dia amb estadístiques

Donada vendes = [12.50, 9.95, 15.90, 15.90, 12.50, 9.95, 15.90], escriu caixa_dia.py que, amb un sol bucle (sense usar sum(), max() ni min(), que veuràs més endavant), calculi i imprimeixi: el total de la caixa, el nombre de vendes, la venda més alta i quantes vendes han superat els 10 €.

Exercici 3: guardiola de reposició

L'Anna aparta diners per reposar El Quixot (15.90 € l'exemplar). Cada dia pot estalviar 4 €. Escriu un while que simuli els dies: acumula 4 € diaris i imprimeix Dia N: M.MM € estalviats fins a tenir prou per comprar 2 exemplars. Al final indica quants dies han calgut.

Solucions

Solució 1:

titols = ["L'Odissea", "Hamlet", "El Quixot"]
preus = [12.50, 9.95, 15.90]

for i, titol in enumerate(titols, start=1):
    preu = preus[i - 1]
    print(f"{i}. {titol:<12}{preu:>6.2f} €")

Com que enumerate comença a 1 però les posicions de la llista comencen a 0, accedim al preu amb preus[i - 1]. Alternativa igual de vàlida: enumerate(titols) sense start, usar preus[i] i imprimir {i + 1}.

Solució 2:

vendes = [12.50, 9.95, 15.90, 15.90, 12.50, 9.95, 15.90]

total = 0.0
num_vendes = 0
venda_maxima = 0.0
vendes_grans = 0

for venda in vendes:
    total += venda
    num_vendes += 1
    if venda > venda_maxima:
        venda_maxima = venda
    if venda > 10:
        vendes_grans += 1

print(f"Total caixa:       {total:.2f} €")
print(f"Nombre de vendes:  {num_vendes}")
print(f"Venda mes alta:    {venda_maxima:.2f} €")
print(f"Vendes > 10 €:     {vendes_grans}")

Quatre variables, quatre patrons: dos acumuladors/comptadors incondicionals (total, num_vendes), un màxim (s'actualitza només si la venda supera la millor vista fins al moment) i un comptador condicional. El total ha de donar 92.60 € i el màxim 15.90 €.

Solució 3:

PREU_QUIXOT = 15.90
ESTALVI_DIARI = 4.0
objectiu = PREU_QUIXOT * 2   # 31.80 €

estalviat = 0.0
dies = 0

while estalviat < objectiu:
    dies += 1
    estalviat += ESTALVI_DIARI
    print(f"Dia {dies}: {estalviat:.2f} € estalviats")

print(f"Objectiu assolit en {dies} dies! ({estalviat:.2f} € per a {objectiu:.2f} €)")

La condició estalviat < objectiu garanteix que el bucle s'atura tan bon punt s'assoleix o se supera la meta: amb 4 € al dia s'arriba a 32 € el dia 8. Fixa't que incrementem dies al començament de cada volta perquè el missatge mostri el dia correcte.

Conclusió

Amb aquesta lliçó, Papyrus ha fet un salt d'escala: ja no treballem llibre a llibre, sinó sobre el catàleg sencer. Has après el for amb range() (inici inclòs, fi exclòs, pas opcional), a recórrer cadenes i llistes, el while per repetir mentre es compleixi una condició, els patrons de comptador i acumulador amb què hem tancat la caixa del dia, els bucles imbricats i l'elegant enumerate() per tenir posició i valor alhora.

Tot i així, els nostres bucles encara són una mica rígids: sempre recorren la seqüència completa, de principi a fi. I si trobem el llibre que buscàvem a mig catàleg i volem parar allà? I si volem saltar-nos els títols esgotats i continuar amb la resta? I si necessitem un menú que es repeteixi indefinidament fins que l'Anna triï "sortir"? Per a tot això existeixen les eines de control de flux — break, continue, else en bucles i la moderna sentència match — que són exactament el tema de la propera lliçó, on construirem el primer menú interactiu de Papyrus.

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