El mòdul 3 va acabar assenyalant una esquerda: el catàleg de Papyrus viu repartit en tres llistes paral·leles (cataleg, preus, estocs) que l'Anna ha de mantenir sincronitzades a mà. Abans de resoldre aquesta esquerda amb diccionaris (lliçó 04-03), necessites dominar de debò l'estructura que has estat fent servir des del mòdul 1 gairebé sense mirar-la: la llista. Fins ara has recorregut llistes amb for, enumerate() i zip(), i has fet servir append() de passada; en aquesta lliçó les obrim per dins: indexació, slicing (llescat), tots els seus mètodes importants i —el més delicat— què vol dir que siguin mutables.

Contingut

  1. Crear llistes
  2. Indexació positiva i negativa
  3. Llescat (slicing) a fons
  4. Mètodes de llistes: afegir, treure i cercar
  5. Ordenar: sort() vs sorted() i reverse()
  6. Mutabilitat: àlies vs còpia
  7. Llistes imbricades
  8. Pertinença: in i not in

Crear llistes

Una llista és una seqüència ordenada i mutable d'elements, que poden ser de qualsevol tipus (fins i tot barrejats, tot i que no sol ser bona idea).

# Les tres llistes paralleles de Papyrus, tal com van quedar al modul 3
cataleg = ["L'Odissea", "Hamlet", "El Quixot"]
preus = [12.50, 9.95, 15.90]
estocs = [4, 6, 8]

buida = []                    # llista buida, a punt per anar-la omplint
tambe_buida = list()          # equivalent
des_de_range = list(range(5)) # [0, 1, 2, 3, 4]: list() converteix iterables
lletres = list("Anna")        # ['A', 'n', 'n', 'a']: una cadena tambe es iterable
  • [] crea una llista literal; list(iterable) construeix una llista a partir de qualsevol cosa recorrible.
  • Recorda del mòdul 2 que també existeixen les comprensions: [p * 1.04 for p in preus] crea una llista nova calculada. Aquí ens centrem a manipular llistes ja existents.

Indexació positiva i negativa

Cada element té una posició (índex) que comença en 0. Els índexs negatius compten des del final: -1 és l'últim element.

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

print(cataleg[0])    # L'Odissea  (primer)
print(cataleg[2])    # El Quixot  (tercer)
print(cataleg[-1])   # El Quixot  (ultim, sense saber quants n'hi ha)
print(cataleg[-2])   # Hamlet     (penultim)
Element Índex positiu Índex negatiu
"L'Odissea" 0 -3
"Hamlet" 1 -2
"El Quixot" 2 -1

Accedir a un índex que no existeix (cataleg[10]) provoca un IndexError que atura el programa. Al mòdul 7 aprendràs a capturar aquest error; de moment, comprova-ho abans amb len().

Llescat (slicing) a fons

El slicing (llescat) extreu una subllista nova amb la sintaxi llista[inici:fi:pas]. Regla d'or: l'inici s'inclou, el fi no.

titols = ["L'Odissea", "Hamlet", "El Quixot", "Faust"]

print(titols[0:2])   # ["L'Odissea", 'Hamlet']          indexs 0 i 1
print(titols[1:])    # ['Hamlet', 'El Quixot', 'Faust']  des d'1 fins al final
print(titols[:2])    # ["L'Odissea", 'Hamlet']          des del principi fins a 1
print(titols[:])     # copia completa (ho veurem a mutabilitat!)
print(titols[::2])   # ["L'Odissea", 'El Quixot']       de dos en dos
print(titols[::-1])  # llista invertida, sense tocar l'original
print(titols[-2:])   # ['El Quixot', 'Faust']           els dos ultims

Punts que convé interioritzar:

  • Ometre inici vol dir "des del principi"; ometre fi, "fins al final".
  • Una llesca mai no dona error per passar-se: titols[1:100] retorna el que hi hagi, sense IndexError.
  • Un pas negatiu recorre cap enrere; [::-1] és l'idiom clàssic per invertir.
  • El resultat és sempre una llista nova: modificar-la no afecta l'original.

Mètodes de llistes: afegir, treure i cercar

Els mètodes es criden amb la sintaxi llista.metode(...). La majoria modifiquen la llista al lloc i retornen None — font clàssica de despistes.

Mètode Què fa Retorna
append(x) Afegeix x al final None
insert(i, x) Insereix x a la posició i, desplaçant la resta None
extend(altra) Afegeix tots els elements d'un altre iterable al final None
remove(x) Elimina la primera aparició de x (error si no hi és) None
pop(i) Elimina i retorna l'element a i (per defecte, l'últim) l'element
index(x) Posició de la primera aparició de x (error si no hi és) int
count(x) Quantes vegades apareix x int

Vegem-los en acció amb l'arribada de "Faust" a Papyrus (de moment amb estoc 0: està esgotat a la distribuïdora):

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

# Alta de "Faust": cal tocar LES TRES llistes, i en el mateix ordre
cataleg.append("Faust")
preus.append(21.00)
estocs.append(0)

print(cataleg.index("Hamlet"))   # 1 → serveix per localitzar el seu preu: preus[1]
print(cataleg.count("Faust"))    # 1

# append vs extend: la diferencia importa
novetats = ["Iliada", "Macbeth"]
copia = cataleg[:]
copia.append(novetats)   # ["L'Odissea", ..., 'Faust', ['Iliada', 'Macbeth']]  llista dins de llista!
copia = cataleg[:]
copia.extend(novetats)   # ["L'Odissea", ..., 'Faust', 'Iliada', 'Macbeth']    elements solts

Fixa't en el cost de manteniment: donar d'alta un llibre exigeix tres append() coordinats, i un remove() oblidat en una de les llistes desincronitza preus i estocs per sempre. Aquest dolor és deliberat: és l'ham que resoldrem a 04-03.

La cua de comandes

Les llistes també serveixen com a cua: les comandes entren pel final (append) i s'atenen pel principi (pop(0)):

cua_comandes = []
cua_comandes.append("Lluis: El Quixot")
cua_comandes.append("Marta: Hamlet")
cua_comandes.append("Lluis: Faust")

while cua_comandes:                      # truthiness: llista buida es False
    comanda = cua_comandes.pop(0)        # treu i retorna la primera
    print(f"Atenent -> {comanda}")

Funciona, però pop(0) obliga Python a desplaçar tots els elements restants una posició. Per a cues llargues existeix una estructura pensada exactament per a això, collections.deque, que veuràs a 04-06.

Ordenar: sort() vs sorted() i reverse()

Aquesta distinció resumeix la filosofia dels mètodes de llista:

llista.sort() sorted(llista)
Què modifica? La llista original, al lloc Res: crea una llista nova
Què retorna? None La llista ordenada
Sobre què funciona? Només llistes Qualsevol iterable (tuples, cadenes...)
Ús típic Quan ja no necessites l'ordre original Quan vols conservar l'original
preus = [12.50, 9.95, 15.90, 21.00]

barats_primer = sorted(preus)            # [9.95, 12.5, 15.9, 21.0]; preus intacte
cars_primer = sorted(preus, reverse=True)

preus.sort()                             # ara si: preus queda ordenat
resultat = preus.sort()                  # compte! resultat es None

cataleg = ["L'Odissea", "Hamlet", "El Quixot", "Faust"]
cataleg.reverse()                        # inverteix al lloc (retorna None)
per_longitud = sorted(cataleg, key=len)  # el key= de les lambdes (03-03) tambe viu aqui

Compte amb les llistes paral·leles: si l'Anna fa cataleg.sort(), els títols es reordenen però preus i estocs no, i el desastre està servit. Amb zip() (03-02) es pot ordenar tot junt, però és un malabarisme més que els diccionaris faran innecessari.

Mutabilitat: àlies vs còpia

Aquí hi ha la conseqüència més important (i traïdora) del fet que les llistes siguin mutables. Assignar una llista a una altra variable no la copia: crea un àlies, un segon nom per a la mateixa llista.

estocs = [4, 6, 8]
inventari = estocs           # ALIES: les dues variables apunten a LA MATEIXA llista

inventari.append(0)
print(estocs)                # [4, 6, 8, 0]  ← estocs tambe ha "canviat"!
print(estocs is inventari)   # True: son el mateix objecte
graph LR
    estocs --> L["[4, 6, 8, 0]"]
    inventari --> L

Per tenir una llista independent cal copiar explícitament:

copia1 = estocs.copy()   # metode copy()
copia2 = estocs[:]       # llesca completa: idiom equivalent
copia3 = list(estocs)    # constructor: tambe copia

copia1.append(99)
print(estocs)            # sense canvis: la copia es una altra llista

Això explica també el parany dels arguments mutables per defecte que vas veure a 03-02: si una funció rep una llista i la modifica, qui la crida nota el canvi, perquè tots dos comparteixen el mateix objecte.

Un matís per deixar apuntat: copy() i [:] fan una còpia superficial (shallow). Si la llista conté altres llistes a dins, es copia la llista exterior però les interiors continuen compartides. Per a còpies profundes existeix copy.deepcopy(); amb saber que existeix ja en tens prou per ara.

Llistes imbricades

Una llista pot contenir llistes. És una manera primitiva d'agrupar la fitxa de cada llibre:

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

print(fitxes[1])        # ['Hamlet', 9.95, 6]  → la fitxa completa
print(fitxes[1][1])     # 9.95                 → primer index: fila; segon: columna
fitxes[2][2] -= 1       # en Lluis compra un Quixot: l'estoc baixa a 7

for titol, preu, estoc in fitxes:   # desempaquetatge al for, com amb zip()
    print(f"{titol:<12} {preu:>6.2f} EUR  ({estoc} ut.)")

Ja és un avenç: cada llibre viatja junt i un sol append(["Faust", 21.00, 0]) dona d'alta tot. Però fitxa[1] i fitxa[2] són índexs muts: res no diu que 1 sigui el preu. La lliçó següent (tuples) i sobretot la 04-03 (diccionaris) poliran aquesta idea.

Pertinença: in i not in

Els operadors in / not in comproven si un valor és a la llista, sense bucles a la vista:

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

if "Faust" in cataleg:
    print("El tenim fitxat (tot i que potser sense estoc).")

if "Dracula" not in cataleg:
    print("Caldria demanar-lo a la distribuidora.")

És una comparació exacta: "faust" in cataleg dona False per les majúscules. Per això cercar_llibre() de papyrus_utils.py compara en minúscules; a 04-05 anirem encara més lluny normalitzant text.

Errors Comuns i Consells

  • Guardar el resultat de sort(), append() o reverse(): llista = llista.sort() deixa llista valent None. Aquests mètodes modifiquen al lloc i retornen None; no reassignis.
  • Confondre àlies amb còpia: b = a no copia. Si després mutes b i a "canvia sola", aquest és el símptoma. Fes servir a.copy() o a[:].
  • remove() i index() amb valors inexistents llancen ValueError. Protegeix-te amb if x in llista: abans de cridar-los (la gestió elegant d'errors arriba al mòdul 7).
  • Modificar una llista mentre la recorres amb for produeix salts d'elements. Recorre una còpia (for x in llista[:]) o construeix una llista nova amb una comprensió.
  • append(altra_llista) quan volies extend(altra_llista): el primer fica la llista sencera com un únic element imbricat.
  • Consell: quan dubtis entre mutar o crear, pregunta't si algú més fa servir aquesta llista. Si l'has rebuda com a argument, crear-ne una de nova (sorted, llescat, comprensió) és gairebé sempre més segur.

Exercicis

  1. Alta i baixa sincronitzades. Partint de les tres llistes paral·leles de Papyrus, escriu una funció alta_llibre(cataleg, preus, estocs, titol, preu, estoc) que afegeixi el llibre a les tres llistes, i una baixa_llibre(cataleg, preus, estocs, titol) que l'elimini de les tres fent servir index() i pop(). Prova de donar d'alta "Faust" (21.00, 0) i de baixa "Hamlet".
  2. Llesques del catàleg. Amb cataleg = ["L'Odissea", "Hamlet", "El Quixot", "Faust"], obtén fent servir només llescat: (a) els dos primers títols, (b) els dos últims, (c) el catàleg invertit, (d) una còpia independent que puguis ordenar alfabèticament sense tocar l'original.
  3. Cua amb prioritat de soci. Simula la cua de comandes com a llista de cadenes "nom: titol". Les comandes d'en Lluís (soci) s'han de colar: escriu-les amb insert(0, ...) en comptes d'append(). Atén la cua amb while i pop(0) mostrant l'ordre final.

Solucions

# Exercici 1
def alta_llibre(cataleg, preus, estocs, titol, preu, estoc):
    """Afegeix un llibre a les tres llistes paralleles (mutant-les)."""
    cataleg.append(titol)
    preus.append(preu)
    estocs.append(estoc)

def baixa_llibre(cataleg, preus, estocs, titol):
    """Elimina un llibre de les tres llistes; retorna el seu preu i estoc."""
    if titol not in cataleg:
        return None                    # sentinella, com a 03-02
    i = cataleg.index(titol)           # mateixa fila a les tres llistes
    cataleg.pop(i)
    return (cataleg, preus.pop(i), estocs.pop(i))[1:]  # (preu, estoc)

cataleg = ["L'Odissea", "Hamlet", "El Quixot"]
preus = [12.50, 9.95, 15.90]
estocs = [4, 6, 8]
alta_llibre(cataleg, preus, estocs, "Faust", 21.00, 0)
print(baixa_llibre(cataleg, preus, estocs, "Hamlet"))   # (9.95, 6)
print(cataleg)   # ["L'Odissea", 'El Quixot', 'Faust']

Fixa't que les funcions muten les llistes rebudes: qui les crida veu els canvis perquè comparteix els objectes. I que cada operació toca tres estructures — l'argument definitiu per a 04-03.

# Exercici 2
cataleg = ["L'Odissea", "Hamlet", "El Quixot", "Faust"]
print(cataleg[:2])         # (a) ["L'Odissea", 'Hamlet']
print(cataleg[-2:])        # (b) ['El Quixot', 'Faust']
print(cataleg[::-1])       # (c) invertit
copia = cataleg[:]         # (d) copia independent
copia.sort()
print(copia)               # ordenada
print(cataleg)             # l'original, intacte
# Exercici 3
cua = []
def encuar(cua, client, titol, soci=False):
    comanda = f"{client}: {titol}"
    if soci:
        cua.insert(0, comanda)   # els socis es colen al principi
    else:
        cua.append(comanda)

encuar(cua, "Marta", "Hamlet")
encuar(cua, "Lluis", "El Quixot", soci=True)
encuar(cua, "Pau", "L'Odissea")

while cua:
    print("Atenent ->", cua.pop(0))
# Lluis: El Quixot / Marta: Hamlet / Pau: L'Odissea

Consell: insert(0, ...) i pop(0) desplacen tota la llista; per a Papyrus és irrellevant, però recorda-ho quan vegis deque a 04-06.

Conclusió

Les llistes són la navalla suïssa de Python: saps crear-les, indexar-les (també des del final), llescar-les amb [inici:fi:pas], i manejar els seus mètodes distingint els que muten al lloc (append, sort, remove...) de les funcions que creen llistes noves (sorted, llescat). Sobretot, ara entens la mutabilitat: b = a crea un àlies, no una còpia, i aquest detall explica comportaments que fins avui semblaven màgia negra. També n'has vist els límits a Papyrus: les llistes paral·leles exigeixen disciplina de sincronització i les llistes imbricades fan servir índexs muts. El pas següent és la seva germana immutable, la tupla: l'estructura perfecta per a registres que no han de canviar — i la que per fi salda la promesa del retorn múltiple de funcions que vam deixar pendent a 03-02.

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