Tancàvem el mòdul 4 amb una promesa: les dues limitacions de TascaFàcil —que només guarda una tasca i que les dades d'aquesta tasca viuen en sis variables soltes— es resolen amb estructures de dades. Aquesta lliçó ataca la primera i més urgent: com guardar molts valors sota un sol nom.

L'eina es diu llista, i és l'estructura de dades que més faràs servir en tota la teva vida com a programador: guarda una seqüència ordenada de valors, creix i minva sobre la marxa, es recorre amb un for i es pot ordenar i filtrar. Tan bon punt la tinguis, la Marta podrà portar alhora el cartell de la fira del llibre, el menú del Forn Solé i el logotip del client Vidal. I de passada tancarem els caps solts de 04-05: sorted(key=...), map i filter necessitaven una col·lecció sobre la qual treballar, i ja la tenim.

Contingut

  1. De les variables soltes a la llista
  2. Array clàssic i llista de Python
  3. Crear llistes, mesurar-les i indexar-les
  4. Talls: [inici:fi:pas]
  5. Mutabilitat, aliasing i còpies
  6. Mètodes i funcions essencials
  7. Recórrer: for, enumerate, zip i in
  8. Comprensions de llistes
  9. sorted amb key, map i filter
  10. TascaFàcil v0.8: llistes paral·leles
  11. Errors comuns i consells
  12. Exercicis
  13. Conclusió

  1. De les variables soltes a la llista

Per guardar els dies estimats de tres tasques, amb el que saps fins ara només hi ha una sortida: dies_1 = 3, dies_2 = 5, dies_3 = 2. Funciona i alhora és un carreró sense sortida: no pots recórrer aquestes variables amb un bucle (cada nom és diferent i no formen cap seqüència), no pots afegir una quarta tasca sense editar el programa, no pots preguntar quantes n'hi ha ni ordenar-les. Una llista converteix aquestes tres variables en un sol objecte amb estructura:

dies = [3, 5, 2]
print(len(dies))        # 3
print(sum(dies))        # 10
dies.append(8)          # una quarta tasca, en temps d'execucio
print(dies)             # [3, 5, 2, 8]

La regla que se'n deriva val per sempre: quan diverses variables es diuen igual llevat d'un número, el que vols és una llista.

  1. Array clàssic i llista de Python

A la literatura de programació apareix la paraula array (de vegades traduïda com a taula o vector), que no és exactament el mateix que una llista de Python. Un array clàssic, tal com existeix en C, Java o Pascal, és un bloc contigu de memòria amb dues restriccions fortes —mida fixa i un únic tipus de dada— a canvi de velocitat i consum mínim de memòria. La llista de Python és dinàmica i heterogènia.

Característica Array clàssic Llista de Python
Mida Fixa en crear-se Creix i minva sola
Tipus dels elements Tots iguals Qualsevol barreja
Accés per índex Sí, des de 0 Sí, des de 0
Inserir al mig Manual i costós insert()
Consum de memòria Mínim Més gran: guarda referències
En Python Mòdul array, NumPy Tipus natiu list

Que ["Cartell fira", 3, True, 4.5] sigui legal no vol dir que convingui. Una llista homogènia —tots els elements del mateix tipus i amb el mateix significat— és molt més fàcil de recórrer, perquè dins del for saps què tens entre mans. Si cada posició significa una cosa diferent, el que busques és una tupla (05-04) o un diccionari (05-03). I quan necessitis milions de números i la memòria importi, existeixen el mòdul array de la biblioteca estàndard i la biblioteca externa NumPy, molt usada en càlcul numèric: no els desenvolupem aquí, però convé saber que hi són.

  1. Crear llistes, mesurar-les i indexar-les

Hi ha tres maneres habituals de crear una llista, i len() per mesurar-la:

equip = ["Marta", "Luis", "Nuria"]      # literal
lletres = list("Alba")                  # amb list(): ['A', 'l', 'b', 'a']
pendents = []                           # buida, per omplir-la despres
print(len(equip), len(pendents))        # 3 0

La llista buida és el punt de partida del patró acumulador de 03-02, només que ara acumules elements en comptes de sumar números. len() és la mateixa funció que feies servir amb cadenes: totes dues són seqüències. Cada element té una posició, i aquesta posició comença a zero:

titols = ["Cartell fira del llibre", "Menu Forn Sole", "Logotip client Vidal"]
print(titols[0])        # Cartell fira del llibre
print(titols[2])        # Logotip client Vidal
print(titols[3])        # IndexError: list index out of range

Una llista de tres elements té índexs 0, 1 i 2: l'últim índex vàlid és sempre len(llista) - 1. Els índexs negatius compten des del final i estalvien calcular longituds: en aquesta llista, -1 és el mateix element que 2, -2 el mateix que 1 i -3 el mateix que 0. Per això titols[-1] és la manera idiomàtica de dir «l'últim», molt millor que titols[len(titols) - 1]. No existeix -0, perquè -0 és 0.

  1. Talls: [inici:fi:pas]

Un tall (slice) extreu un tros i retorna una llista nova. Els tres números són opcionals. La regla clau, i la font eterna de confusió: inici s'inclou, fi NO. L'avantatge del conveni és que fi - inici dóna directament quants elements en surten. Els exemples parteixen de dies = [3, 5, 2, 8, 1, 6].

Expressió Resultat Explicació
dies[1:4] [5, 2, 8] De l'1 al 3; el 4 queda fora
dies[:3] [3, 5, 2] Des del principi
dies[3:] [8, 1, 6] Fins al final
dies[-2:] [1, 6] Els dos últims
dies[::2] [3, 2, 1] De dos en dos, des del principi
dies[::-1] [6, 1, 8, 2, 5, 3] Al revés
dies[:] [3, 5, 2, 8, 1, 6] Còpia completa
dies[4:2] [] Rang impossible: buida, sense error

Dos detalls importants. Un tall mai no dóna IndexError encara que et passis de llarg: dies[2:99] retorna el que hi hagi. I dies[:] sembla redundant, però és la manera clàssica d'obtenir una còpia superficial.

  1. Mutabilitat, aliasing i còpies

Les llistes són mutables: pots canviar-ne el contingut sense crear una llista nova, cosa que amb les cadenes no podies (02-01). prioritats[1] = "alta" substitueix el segon element i ja està. Fins aquí, còmode. Ara el que sorprèn tothom:

originals = ["Cartell fira", "Menu Sole"]
copia = originals               # de debo es una copia?
copia.append("Logotip Vidal")
print(originals)                # ['Cartell fira', 'Menu Sole', 'Logotip Vidal']
print(copia is originals)       # True

Hem modificat copia i ha canviat originals. El motiu és el que vas estudiar a 04-03 amb les referències: copia = originals no copia la llista, copia la referència; els dos noms apunten al mateix objecte, i això s'anomena aliasing. Per copiar de debò hi ha tres formes equivalents, que veuràs sota el diagrama.

graph LR
    A[originals] --> L["Llista A en memoria<br/>Cartell fira, Menu Sole"]
    B[copia] --> L
    C[de_debo] --> M["Llista B en memoria<br/>Cartell fira, Menu Sole"]
copia_1 = originals.copy()      # la mes explicita i recomanable
copia_2 = list(originals)       # constructor a partir de l'original
copia_3 = originals[:]          # tall complet, el classic
print(len(originals), copia_1 is originals)     # 2 False

Això té una conseqüència directa sobre les funcions, i és nova respecte a 04-03. Allà vam veure que passar un argument immutable no permet a la funció tocar la variable de qui la crida. Amb una llista és al revés:

def afegir_tasca(llista, titol):
    """Afegeix un titol a la llista rebuda (la modifica)."""
    llista.append(titol)

def buidar_malament(llista):
    llista = []             # reassigna el nom LOCAL: no es veu fora

def buidar_be(llista):
    llista.clear()          # modifica l'objecte: si que es veu fora

agenda = ["Cartell fira"]
afegir_tasca(agenda, "Menu Sole")
print(agenda)       # ['Cartell fira', 'Menu Sole']

La funció rep una referència a la mateixa llista, així que els seus canvis es veuen fora. És potentíssim —així afegirà tasques la v0.8— i també perillós: una funció que modifica en silenci una llista aliena provoca sorpreses. Regla pràctica: o modifica i ho diu en el seu nom (afegir_tasca, ordenar_agenda), o treballa sobre una còpia i retorna el resultat. I fixa't en el contrast entre buidar_malament i buidar_be: reassignar el paràmetre només canvia el nom local, mentre que cridar un mètode canvia l'objecte que tots dos comparteixen.

  1. Mètodes i funcions essencials

Els mètodes es criden sobre la llista amb un punt i gairebé tots la modifiquen al lloc; les funcions integrades es criden amb la llista dins dels parèntesis i retornen un valor sense tocar-la.

Mètode Què fa Retorna
.append(x) Afegeix x al final None
.insert(i, x) Insereix x a la posició i None
.extend(altra) Afegeix tots els elements d'altra None
.remove(x) Esborra la primera aparició de x (ValueError si no hi és) None
.pop(i) Treu l'element i (l'últim si s'omet) L'element
.clear() Buida la llista None
.index(x) Posició de la primera aparició (ValueError si no hi és) Enter
.count(x) Quantes vegades apareix x Enter
.sort() Ordena al lloc None
.reverse() Inverteix l'ordre al lloc None
tasques = ["Cartell fira", "Menu Sole"]
tasques.insert(0, "Revisar correu")         # es cola en primera posicio
tasques.extend(["Targetes Vidal", "Flyer"]) # afegeix dues de cop
print(tasques.index("Menu Sole"), tasques.count("Flyer"))   # 2 1
ultima = tasques.pop()                      # treu 'Flyer' i el retorna
tasques.remove("Menu Sole")                 # esborra per valor, no per posicio
malament = tasques.sort()                   # sort() retorna None
print(malament)                             # None -> has perdut la llista

Diferència decisiva: pop esborra per posició i retorna l'element; remove esborra per valor i no retorna res. I el parany número u de les llistes és aquesta última línia: els mètodes que modifiquen retornen None, així que mai no guardis el seu resultat. Les funcions integrades, en canvi, retornen alguna cosa nova i no toquen la llista:

dies = [3, 5, 2, 8, 1]
print(len(dies), sum(dies), min(dies), max(dies))   # 5 19 1 8
print(f"Mitjana: {sum(dies) / len(dies):.2f}")      # Mitjana: 3.80
print(sorted(dies), dies)   # [1, 2, 3, 5, 8] [3, 5, 2, 8, 1] -> intacta

La regla per triar: .sort() si vols que la llista quedi ordenada; sorted() si necessites conservar l'ordre original. Tots dos admeten reverse=True. Com ordenen per dins i quant costa és matèria d'Algorismes d'ordenació i d'Eficiència i notació Big-O; aquí els fem servir com a eines del llenguatge.

  1. Recórrer: for, enumerate, zip i in

El for de 03-02 està fet per a això. Recórrer la llista directament —for titol in titols:— és la forma preferent en Python: a cada volta, titol pren el valor d'un element, sense índexs, sense len() i sense possibilitat d'IndexError. Quan sí que necessites la posició —numerar un llistat, per exemple— fes servir enumerate(), que produeix dos valors per volta:

for numero, titol in enumerate(titols, start=1):
    print(f"{numero}. {titol}")     # 1. Cartell fira del llibre, 2. Menu...

start=1 fa que la numeració que veu l'usuari comenci a 1 encara que els índexs interns continuïn començant a 0: just el que necessita un menú. Quan tens dues llistes en paral·lel, zip() les aparella:

responsables = ["Marta", "Luis", "Nuria"]
for titol, responsable in zip(titols, responsables):
    print(f"{titol:<24}{responsable:>10}")
print("Marta" in responsables)          # True
print("Alba" not in responsables)       # True

zip s'atura amb la llista més curta sense avisar: si una en té un element de més, aquest element desapareix del recorregut. Les dues últimes línies fan servir l'operador in de 02-02 per comprovar pertinença. Com busca in per dins, i per què en una llista llarga triga més que en un conjunt, es veu a Algorismes de cerca i a Diccionaris i conjunts.

  1. Comprensions de llistes

Aquest patró apareix constantment: crear una llista buida (dobles = []), recórrer una altra seqüència i afegir alguna cosa a cada volta (dobles.append(d * 2)). Python ofereix una forma condensada d'escriure exactament això, la comprensió de llista, que es llegeix de dins cap enfora: per a cada d de dies, calcula d * 2 i posa'l a la llista.

dobles = [d * 2 for d in dies]              # transformar: [6, 10, 4, 16, 2]
llargues = [d for d in dies if d > 3]       # filtrar: [5, 8]
altes = [t.upper() for t in titols if "fira" in t]      # filtrar i transformar
Forma Significat
[f(x) for x in llista] Transformar tots els elements
[x for x in llista if cond] Quedar-se amb alguns
[f(x) for x in llista if cond] Filtrar i transformar

I ara la part que gairebé mai no s'explica: quan NO fer-les servir. No la facis servir si l'expressió no cap còmodament en una línia, si necessites condicions imbricades, si a dins cal imprimir o demanar dades a l'usuari, o si encadenes dos o tres for. Una cosa com [t.upper() if len(t) > 10 else t.lower() for t in titols if "e" in t] és legal i és il·legible: demana a crits un bucle normal. L'objectiu no era escriure menys, sinó llegir millor.

  1. sorted amb key, map i filter

A 04-05 va quedar pendent l'ordre superior sobre col·leccions. Ja el podem tancar. sorted(llista, key=funcio) ordena segons el que retorni funcio aplicada a cada element, no segons l'element en si:

titols = ["Menu Sole", "Cartell fira del llibre", "Flyer", "Logotip Vidal"]
print(sorted(titols, key=len))
# ['Flyer', 'Menu Sole', 'Logotip Vidal', 'Cartell fira del llibre']

PRIORITATS = ("alta", "mitjana", "baixa")
prioritats = ["baixa", "alta", "mitjana", "alta"]
print(sorted(prioritats, key=lambda p: PRIORITATS.index(p)))
# ['alta', 'alta', 'mitjana', 'baixa']

Amb una lambda pots inventar-te qualsevol criteri. Ordenar les prioritats alfabèticament donaria alta, baixa, mitjana, que no és el seu ordre lògic; en canvi PRIORITATS.index(p) retorna 0 per a "alta", 1 per a "mitjana" i 2 per a "baixa", i ordenar per aquest número dóna l'ordre correcte. És exactament el de 04-05: sorted no sap res de prioritats, només d'ordenar; el criteri s'injecta des de fora. I key=str.lower ordena ignorant majúscules, mentre que reverse=True inverteix el resultat.

map(funcio, llista) aplica una funció a tots els elements i filter(funcio, llista) es queda amb aquells per als quals retorna True. Totes dues retornen un objecte mandrós que cal convertir amb list():

Objectiu Amb comprensió Amb map/filter
Transformar [d * 2 for d in dies] list(map(lambda d: d * 2, dies))
Filtrar [d for d in dies if d > 3] list(filter(lambda d: d > 3, dies))

En Python modern es prefereix la comprensió: més curta, sense list() i més llegible. map i filter continuen sent útils quan la funció ja existeix i té nom (map(str.upper, titols) és netíssim), i els has de reconèixer perquè abunden en el codi aliè.

  1. TascaFàcil v0.8: llistes paral·leles

La v0.7 guardava una tasca en sis variables soltes. La v0.8 guarda moltes tasques en cinc llistes paral·leles: l'element d'índex i de cada llista es refereix a la mateixa tasca.

graph LR
    T["titols"] --> I0["L'index 0 de les quatre llistes<br/>descriu la mateixa tasca"]
    R["responsables"] --> I0
    P["prioritats"] --> I0
    C["completades"] --> I0

Les funcions d'entrada (demanar_text, demanar_opcio, demanar_enter, confirmar), la de lògica (classificar_urgencia) i mostrar_menu/pausar es conserven intactes. Canvien les d'acció i el main():

# tascafacil.py - Estudi Alba / Versio 0.8: moltes tasques en llistes paralleles

def mostrar_llistat(titols, responsables, prioritats, completades):
    """Pinta totes les tasques numerades des de l'1."""
    if len(titols) == 0:
        print("No hi ha cap tasca registrada.")
        return
    for numero, titol in enumerate(titols, start=1):
        i = numero - 1
        marca = "[X]" if completades[i] else "[ ]"
        print(f"{numero:>2}. {marca} {titol:<22}{responsables[i]:>8}{prioritats[i]:>7}")
    print("-" * AMPLE)
    print(f"Total: {len(titols)} tasques, {completades.count(True)} completades.")

def registrar_tasca(titols, responsables, prioritats, dies, completades):
    """Demana una tasca nova i l'afegeix al final de les cinc llistes."""
    titols.append(demanar_text("Titol         : "))
    responsables.append(demanar_opcio("Responsable   : ", EQUIP).capitalize())
    prioritats.append(demanar_opcio("Prioritat     : ", PRIORITATS))
    dies.append(demanar_enter("Dies (1-365)  : ", 1, 365))
    completades.append(False)
    print(f"Tasca {len(titols)} registrada correctament.")

def triar_index(titols):
    """Demana el numero d'una tasca i retorna el seu index, o -1 si no n'hi ha cap."""
    if len(titols) == 0:
        print("No hi ha cap tasca registrada.")
        return -1
    return demanar_enter(f"Numero de tasca (1-{len(titols)}): ", 1, len(titols)) - 1

def main():
    """Executa el bucle principal de l'aplicacio."""
    titols, responsables, prioritats, dies, completades = [], [], [], [], []
    while True:
        mostrar_menu()
        opcio = demanar_opcio("Tria una opcio (1-5): ", OPCIONS)
        if opcio == "5":
            if confirmar("Segur que vols sortir?"):
                print("Fins despres. TascaFacil es tanca.")
                break
        elif opcio == "1":
            registrar_tasca(titols, responsables, prioritats, dies, completades)
        elif opcio == "2":
            mostrar_llistat(titols, responsables, prioritats, completades)
        elif opcio == "3":
            i = triar_index(titols)
            if i >= 0:
                prioritats[i] = demanar_opcio("Nova prioritat: ", PRIORITATS)
                print(f"Prioritat de '{titols[i]}' actualitzada.")
        elif opcio == "4":
            i = triar_index(titols)
            if i >= 0 and completades[i]:
                print("Aquesta tasca ja estava completada.")
            elif i >= 0:
                completades[i] = True
                print(f"'{titols[i]}' completada. Bona feina.")
        pausar()

if __name__ == "__main__":
    main()

El que ha canviat i per què:

  • registrar_tasca ja no retorna res: modifica les llistes que rep. Ho pot fer perquè les llistes són mutables (secció 5); el nom i el docstring ho anuncien.
  • triar_index tradueix el número que veu l'usuari a l'índex intern restant 1. Aquest - 1 és la frontera entre «primera tasca» per a la Marta i «índex 0» per a Python, i convé tenir-la en un únic lloc.
  • enumerate(..., start=1) numera el llistat sense comptadors manuals i completades.count(True) compta les fetes sense bucle. A més desapareix la bandera hi_ha_tasca: len(titols) == 0 respon al mateix i encara diu quantes n'hi ha.

I ara el problema nou que hem creat. Les cinc llistes han d'estar sincronitzades sempre: si algú afegeix un camp i oblida un append, es desalineen i el responsable d'una tasca apareix al costat del títol d'una altra. No hi ha error ni avís: el programa continua funcionant i mentint. Esborrar una tasca exigeix cinc pop coordinats, i afegir un camp obliga a tocar la signatura de totes les funcions. Les cinc llistes paral·leles són, en realitat, una sola llista de tasques que encara no sabem escriure.

Errors Comuns i Consells

Confondre índex i posició humana. El tercer element és llista[2]. Aquest desfasament d'un (off-by-one) és l'error més freqüent de l'ofici: en pintar per a l'usuari, enumerate(..., start=1); en accedir, resta 1 en un únic lloc. Esperar que .sort() retorni alguna cosa. ordenada = llista.sort() deixa ordenada valent None; si necessites una llista nova, sorted(llista). El mateix amb append, reverse, insert i clear. I .remove(x) i .index(x) amb un valor que no hi és llancen ValueError: comprova-ho abans amb if x in llista:.

Creure que copia = llista copia. Copia la referència. Fes servir .copy(), list() o [:], i comprova amb is si tens dubtes. I no esborris elements mentre recorres la llista: si dins d'un for fas llista.remove(x), el recorregut se salta elements perquè les posicions es desplacen sota els teus peus. La solució segura és construir una llista nova amb els que vols conservar, normalment amb una comprensió.

Consell: fes servir el for directe sempre que puguis. for t in titols: és més clar i més segur que for i in range(len(titols)):. I mantén les llistes homogènies: una llista conté «moltes coses del mateix tipus», no «una cosa repartida en trossos».

Exercicis

Exercici 1: Predir la sortida

Digues què imprimeix cada print i explica per què.

dies = [3, 5, 2, 8, 1, 6]
print(dies[2], dies[-1], dies[1:3])
copia = dies
copia.append(4)
print(len(dies))
altra = dies[:]
altra.clear()
print(len(dies), len(altra))
print(dies.sort(), dies[0])

Exercici 2: Estadístiques de l'estudi

Amb les llistes de sota, imprimeix: el nombre de tasques, el total i la mitjana de dies, el títol de la tasca més llarga, els títols de prioritat "alta" i els títols ordenats per dies de major a menor. Fes servir comprensions i sorted(key=...) on encaixin.

titols = ["Cartell fira", "Menu Sole", "Logotip Vidal", "Flyer estiu"]
responsables = ["Marta", "Luis", "Nuria", "Marta"]
prioritats = ["alta", "mitjana", "alta", "baixa"]
dies = [3, 5, 2, 8]

Exercici 3: Eliminar una tasca

Escriu eliminar_tasca(titols, responsables, prioritats, dies, titol), que esborri de les quatre llistes la tasca amb aquest títol sense desalinear-les i avisi si no existeix. Explica després, en dues frases, per què aquesta funció resulta incòmoda d'escriure i de mantenir.

Solucions

Solució 1.

2 6 [5, 2]
7
7 0
None 1

dies[2] és el tercer element, dies[-1] l'últim i dies[1:3] inclou els índexs 1 i 2 però no el 3. copia = dies és aliasing: l'append afecta l'única llista que hi ha i len(dies) passa de 6 a 7. altra = dies[:] sí que és una còpia, així que buidar-la no toca l'original: 7 i 0. I .sort() ordena al lloc retornant None; després d'ordenar, el primer element és el menor, 1.

Solució 2.

print(f"Tasques: {len(titols)}")
print(f"Total: {sum(dies)} dies, mitjana {sum(dies) / len(dies):.2f}")
print(f"La mes llarga: {titols[dies.index(max(dies))]} ({max(dies)} dies)")

altes = [titols[i] for i in range(len(titols)) if prioritats[i] == "alta"]
print(f"Prioritat alta: {', '.join(altes)}")

per_dies = sorted(titols, key=lambda t: dies[titols.index(t)], reverse=True)
print(per_dies)     # ['Flyer estiu', 'Menu Sole', 'Cartell fira', 'Logotip Vidal']

Surten 4 tasques, 18 dies en total, mitjana 4.50, la més llarga «Flyer estiu» i dues de prioritat alta. dies.index(max(dies)) dóna la posició del màxim, i aquesta posició serveix per llegir el títol corresponent: així funcionen les llistes paral·leles. Fixa't en la retorçada que queda l'última línia: per ordenar per dies cal buscar cada títol a la seva pròpia llista per esbrinar-ne l'índex. Amb una llista de diccionaris (05-04) serà sorted(agenda, key=lambda t: t["dies"]) i s'ha acabat.

Solució 3.

def eliminar_tasca(titols, responsables, prioritats, dies, titol):
    """Elimina de les quatre llistes la tasca amb el titol indicat."""
    if titol not in titols:
        print(f"No existeix cap tasca anomenada '{titol}'.")
        return False
    i = titols.index(titol)
    titols.pop(i)
    responsables.pop(i)
    prioritats.pop(i)
    dies.pop(i)
    return True

És incòmoda per dos motius. Primer, rep quatre llistes només per esborrar una tasca: si demà afegeixes un camp, cal canviar la signatura i totes les crides. Segon, la seva correcció depèn que els quatre pop s'executin sempre junts i amb el mateix índex; n'hi ha prou d'oblidar-ne un perquè totes les tasques posteriors quedin barrejades sense cap error que ho delati.

Conclusió

Una llista guarda molts valors sota un sol nom i resol el que les variables soltes no podien: comptar, recórrer, sumar, ordenar i créixer en temps d'execució. S'hi accedeix per índex des de 0, amb índexs negatius per comptar des del final i IndexError si et passes; es trosseja amb talls [inici:fi:pas], on el final mai no s'inclou. És mutable, i d'aquí l'aliasing: copia = llista no copia res, i per copiar de debò hi ha .copy(), list() i [:]. Els seus mètodes modifiquen al lloc i retornen None, mentre que len, sum, min, max i sorted retornen un valor nou. Es recorre amb for, amb enumerate quan cal la posició i amb zip quan hi ha diverses llistes; in comprova pertinença. Les comprensions condensen el patró «crear, recórrer, afegir» sempre que la línia continuï sent llegible. I amb sorted(key=...), map i filter hem tancat el capítol de l'ordre superior de 04-05.

TascaFàcil ha arribat a la v0.8 i per fi gestiona la feina real de l'estudi: registra tantes tasques com calgui, les llista numerades i permet canviar la prioritat o completar l'enèsima. Però ho ha aconseguit amb cinc llistes paral·leles que cal sincronitzar a mà, i ja has vist com de fràgil és: un pop oblidat i el llistat comença a mentir en silenci. El problema no és la llista, és que l'estem fent servir per a una cosa que no li toca: una llista guarda moltes coses iguals, i el que necessitem és agrupar els camps diferents d'una mateixa tasca sota un nom. Això arriba a Diccionaris i conjunts i es remata a Tuples i estructures imbricades. Abans, a Cadenes de caràcters, mirarem de prop l'altra seqüència que fas servir des del mòdul 2 sense veure-la com a tal —el text— i n'exprimirem els mètodes: els títols, els responsables i els codis de tasca d'Estudi Alba són tots cadenes, i hi ha molt a treure'n.

© Copyright 2026. Tots els drets reservats