En tancar la lliçó anterior quedava un assemblatge pendent. La v0.8 sabia portar moltes tasques, però repartides en llistes paral·leles que calia sincronitzar a mà; la v0.9 sap descriure una tasca impecablement, però només en porta una. Les dues peces encaixen de manera gairebé òbvia: si una tasca és un diccionari i una llista guarda moltes coses del mateix tipus, aleshores l'agenda d'Estudi Alba és una llista de diccionaris. Aquesta lliçó tracta d'això —de combinar estructures— i de l'última que faltava per conèixer: la tupla, una seqüència immutable que ja t'has trobat sense saber-ho cada vegada que una funció retornava dos valors a 04-02. En acabar sabràs llegir sense dubtar expressions com agenda[0]["responsable"], triar amb criteri entre llista, diccionari i tupla, i TascaFàcil haurà assolit la v0.10 amb tot el seu menú funcionant sobre una col·lecció real.
Contingut
- Què és una tupla
- Immutabilitat: quan tupla i quan llista
- Desempaquetatge
- Retornar diversos valors és retornar una tupla
- Tuples com a claus i
namedtuple - Estructures imbricades: els tres patrons
- Llegir una expressió encadenada
- Recórrer estructures imbricades
- Còpia superficial i còpia profunda
- Triar l'estructura adequada
- TascaFàcil v0.10: l'agenda de l'estudi
- Errors comuns i consells
- Exercicis
- Conclusió
- Què és una tupla
Una tupla és una seqüència ordenada de valors, com una llista, però immutable: un cop creada no s'hi pot afegir, treure ni canviar res. S'escriu amb parèntesis, encara que el que de debò crea la tupla són les comes.
tasca = ("Cartell fira", "Marta", "alta", 3)
print(tasca[0], tasca[-1], len(tasca)) # Cartell fira 3 4
print(tasca[1:3], "Marta" in tasca) # ('Marta', 'alta') True
tasca[3] = 5 # TypeError: 'tuple' object does not support item assignment
sense_parentesis = "Cartell fira", "Marta", 3 # tambe es una tupla
nomes_un = ("Cartell fira",) # tupla d'UN element: la coma es obligatoria
no_es_tupla = ("Cartell fira") # nomes una cadena entre parentesis
print(type(nomes_un), type(no_es_tupla)) # <class 'tuple'> <class 'str'>Índexs, talls, len(), in i el recorregut amb for funcionen exactament igual que en una llista; l'única cosa que no funciona és modificar. Les tres últimes assignacions són les que despisten tothom: els parèntesis són opcionals, la tupla buida s'escriu () i la tupla d'un element exigeix la coma final, perquè sense ella els parèntesis són d'agrupació i el resultat és el valor de dins. Les constants que fas servir des de 04-04 —PRIORITATS = ("alta", "mitjana", "baixa")— són precisament tuples, i ara saps per què: són dades fixes que el programa no ha de canviar mai.
- Immutabilitat: quan tupla i quan llista
| Criteri | Tupla | Llista |
|---|---|---|
| Sintaxi | (1, 2, 3) |
[1, 2, 3] |
| Es pot modificar? | No | Sí |
| Mètodes disponibles | Només count i index |
append, pop, sort… |
| Clau de diccionari o element d'un conjunt? | Sí | No |
| Ús típic | Registre de camps fixos, constants, valors retornats | Col·lecció que creix i canvia |
La pregunta que ho decideix és: els elements són coses diferents amb un paper fix, o moltes coses iguals? Una coordenada (x, y), una data (2026, 8, 4) o una fila de dades són tuples: nombre fix de posicions, cadascuna amb el seu significat. Els responsables de l'estudi són una llista: pot créixer, minvar i reordenar-se. La immutabilitat té tres avantatges pràctics: protegeix les dades que no han de canviar, permet fer servir la tupla com a clau de diccionari o element d'un conjunt, i documenta la intenció a qui llegeix el codi, perquè una tupla diu «això és un registre tancat». La contrapartida és que per canviar alguna cosa cal construir una tupla nova.
- Desempaquetatge
Desempaquetar és repartir els elements d'una seqüència en diverses variables d'una sola vegada. És una de les coses que fan que Python sigui còmode, i ja ho has fet servir sense anomenar-ho a for clau, valor in tasca.items().
tasca = ("Cartell fira", "Marta", "alta", 3)
titol, responsable, prioritat, dies = tasca
print(responsable, dies) # Marta 3
a, b = 3, 5
a, b = b, a # intercanvi sense variable auxiliar: 5 3
primer, *resta = ["Marta", "Luis", "Nuria", "Alba"]
print(primer, resta) # Marta ['Luis', 'Nuria', 'Alba']
primer, *mig, ultim = ["Marta", "Luis", "Nuria", "Alba"] # mig: ['Luis','Nuria']
titol, _, prioritat, _ = tasca # el guio baix: 'aquest valor no importa'El nombre de noms a l'esquerra ha de coincidir exactament amb el d'elements; si no, ValueError: too many values to unpack. L'intercanvi en una línia funciona perquè el costat dret s'avalua sencer abans d'assignar: primer es construeix la tupla (5, 3) i després es reparteix. L'asterisc recull «la resta» en una llista, i només n'hi pot haver un per desempaquetatge. El guió baix _ és una convenció, no una regla del llenguatge: una variable normal el nom de la qual anuncia que el seu valor s'ignorarà.
- Retornar diversos valors és retornar una tupla
A 04-02 vas aprendre que una funció pot retornar diversos valors i va quedar dit que s'explicaria aquí. Ja tens totes les peces: return a, b construeix una tupla i la retorna; l'assignació múltiple en la crida és un desempaquetatge.
def analitzar(dies):
"""Retorna el total, la mitjana i el maxim d'una llista de dies."""
return sum(dies), sum(dies) / len(dies), max(dies)
total, mitjana, maxim = analitzar([3, 5, 2, 8]) # desempaquetatge
resultat = analitzar([3, 5, 2, 8]) # sense desempaquetar
print(resultat, type(resultat)) # (18, 4.5, 8) <class 'tuple'>
print(resultat[1]) # 4.5És a dir: una funció sempre retorna un únic valor; el que passa és que aquest valor pot ser una tupla amb diverses coses a dins. Aquella registrar_tasca() de la v0.7, que «retornava quatre valors», retornava en realitat una tupla de quatre elements que es desempaquetava en la crida. Consell de disseny: si tornes d'una funció amb més de tres o quatre valors, o si qui la crida ha de recordar l'ordre exacte, probablement el que vols retornar és un diccionari, on cada dada porta el seu nom. És just el canvi que va fer la v0.9.
- Tuples com a claus i
namedtuple
namedtupleEn ser immutable, una tupla pot ser clau d'un diccionari. Això permet indexar per una combinació de dades:
hores = {("Marta", "dilluns"): 6, ("Marta", "dimarts"): 4, ("Luis", "dilluns"): 8}
print(hores[("Marta", "dimarts")]) # 4
print(hores.get(("Nuria", "dilluns"), 0)) # 0 -> no va treballar aquell diaAmb una llista com a clau seria impossible (TypeError: unhashable type). Aquest patró és habitual per a taules de doble entrada: persona i dia, producte i mes, fila i columna. Existeix també namedtuple, del mòdul collections, que és una tupla els camps de la qual tenen nom: Tasca = namedtuple("Tasca", "titol responsable dies") permet escriure t.responsable en lloc de t[1]. La menciono perquè la reconeguis si la trobes; en aquest curs farem servir diccionaris per al mateix, i al mòdul 7 veuràs l'eina definitiva, les classes.
- Estructures imbricades: els tres patrons
Llistes, diccionaris i tuples es poden ficar uns dins dels altres sense límit, però a la pràctica gairebé tot el que necessitaràs són tres combinacions. La primera és la llista de diccionaris —«molts registres»—, la forma natural d'una agenda, un catàleg o una taula:
agenda = [
{"titol": "Cartell fira", "responsable": "Marta", "prioritat": "alta", "dies": 3},
{"titol": "Menu Sole", "responsable": "Luis", "prioritat": "mitjana", "dies": 5},
{"titol": "Logotip Vidal", "responsable": "Nuria", "prioritat": "baixa", "dies": 2},
]Diccionari de llistes —«grups de coses», quan vols anar directe al nom del grup. I diccionari de diccionaris —«fitxes indexades per un identificador únic»:
per_responsable = {"Marta": ["Cartell fira", "Flyer estiu"],
"Luis": ["Menu Sole"], "Nuria": []}
equip = {"Marta": {"rol": "coordinadora", "hores": 38, "etiquetes": {"disseny"}},
"Luis": {"rol": "dissenyador", "hores": 35, "etiquetes": {"impremta"}}}S'hi accedeix amb agenda[0]["titol"], per_responsable["Marta"][0] i equip["Marta"]["rol"] respectivament. La taula de decisió completa és a la secció 10.
- Llegir una expressió encadenada
Una expressió com agenda[0]["responsable"] fa por la primera vegada i deixa de fer-ne tan bon punt saps que es llegeix d'esquerra a dreta, un pas cada vegada:
agenda→ la llista completa de tasques.agenda[0]→ el primer element d'aquesta llista, que resulta ser un diccionari.agenda[0]["responsable"]→ el valor de la clau"responsable"d'aquest diccionari,"Marta".
graph TD
A["agenda (llista)"] --> B["[0] diccionari"]
A --> C["[1] diccionari"]
B --> D["titol: Cartell fira"]
B --> E["responsable: Marta"]
C --> G["titol: Menu Sole"]
C --> H["responsable: Luis"]
El truc per no perdre't: cada claudàtor baixa un nivell de l'arbre. Si dubtes, imprimeix els passos intermedis al REPL: print(agenda[0]) ensenya el diccionari sencer i print(type(agenda[0])) diu en quin tipus estàs aturat. Els errors són informatius: TypeError: list indices must be integers significa que has fet servir una clau de text on hi havia una llista, i KeyError, que la clau no existeix en aquell diccionari. Amb la pràctica es llegeixen d'una ullada: agenda[1]["titol"] és "Menu Sole", equip["Marta"]["rol"] és "coordinadora", per_responsable["Marta"][1] és "Flyer estiu", i agenda[0]["dies"] = 4 modifica la tasca al lloc sense cap problema.
- Recórrer estructures imbricades
Recórrer una llista de diccionaris és un for normal en què cada element resulta ser un diccionari:
for tasca in agenda:
print(f"{tasca['titol']:<20}{tasca['responsable']:>8}{tasca['dies']:>4}d")
pendents = [t for t in agenda if t["dies"] > 2] # filtrar
titols = [t["titol"] for t in agenda] # extreure una columna
per_dies = sorted(agenda, key=lambda t: t["dies"], reverse=True) # ordenarAquí es veu per què valia la pena el canvi: ordenar per dies, que a 05-01 era una expressió retorçada amb titols.index(t), ara és simplement key=lambda t: t["dies"]. Recórrer un diccionari de llistes exigeix dos nivells de bucle, el segon dins del primer; amb un diccionari de diccionaris el patró és el mateix:
for responsable, titols in per_responsable.items():
print(f"{responsable} ({len(titols)} tasques):")
for titol in titols:
print(f" - {titol}")
for nom, fitxa in equip.items():
print(f"{nom:<8}{fitxa['rol']:<15}{fitxa['hores']:>3}h")Fixa't en el desempaquetatge directe a la capçalera: for responsable, titols in ...items() separa clau i valor sense línies extra, i funciona sempre que l'estructura interna sigui coneguda.
- Còpia superficial i còpia profunda
Aquí apareix un error clàssic que només es manifesta amb estructures imbricades: .copy() copia la llista, però no copia el que hi ha a dins.
import copy
copia = agenda.copy() # copia superficial
copia[0]["dies"] = 99 # modifiquem la tasca a traves de la copia
print(agenda[0]["dies"]) # 99 -> l'original tambe ha canviat
de_debo = copy.deepcopy(agenda) # copia profunda
de_debo[0]["dies"] = 1
print(agenda[0]["dies"]) # 99 -> ara si, l'original no s'ha tocatLa llista copia és nova, però els seus elements són els mateixos diccionaris que els d'agenda: s'ha duplicat la caixa, no el contingut.
| Tipus de còpia | Com | Què duplica |
|---|---|---|
| Referència (aliasing) | b = a |
Res: dos noms, un objecte |
| Superficial | a.copy(), list(a), a[:] |
El contenidor exterior |
| Profunda | copy.deepcopy(a) |
Tot l'arbre, a qualsevol profunditat |
La regla pràctica: si l'estructura té un sol nivell, la còpia superficial n'hi ha prou; si hi ha llistes o diccionaris a dins i els modificaràs, necessites deepcopy. I no facis servir deepcopy per defecte: és més lenta, i la majoria de vegades el que vols precisament és que la funció modifiqui l'agenda de debò.
- Triar l'estructura adequada
| Necessito… | Estructura | Exemple de l'estudi |
|---|---|---|
| Moltes coses del mateix tipus, en ordre | Llista | Títols de tasques |
| Els camps d'una cosa, cadascun amb el seu nom | Diccionari | Una tasca |
| Un registre fix que no ha de canviar | Tupla | PRIORITATS, una coordenada |
| Valors únics, només comprovar pertinença | Conjunt | Etiquetes de l'estudi |
| Molts registres amb els mateixos camps | Llista de diccionaris | L'agenda completa |
| Agrupar registres per una categoria | Diccionari de llistes | Tasques per responsable |
| Buscar una fitxa per identificador únic | Diccionari de diccionaris | Fitxa de l'equip |
| Indexar per una combinació de dades | Diccionari amb claus tupla | Hores per persona i dia |
Un consell que estalvia molta feina: tria l'estructura pensant en l'operació més freqüent. Si el que més fas és recórrer-ho tot i ordenar, llista de diccionaris; si el que més fas és «dóna'm la fitxa de X», diccionari de diccionaris. I si necessites les dues coses, mantenir una llista i construir l'agrupament quan calgui sol ser més simple que sostenir dues estructures sincronitzades —l'error que ja vam cometre amb les llistes paral·leles.
- TascaFàcil v0.10: l'agenda de l'estudi
Arribem a l'assemblatge: l'agenda és una llista de diccionaris i totes les opcions del menú es refan sobre ella, que creix a sis per incloure el resum per responsable. Les llistes paral·leles de la v0.8 desapareixen per sempre.
# tascafacil.py - Estudi Alba / Versio 0.10: l'agenda es una llista de diccionaris
AMPLE = 52
PRIORITATS = ("alta", "mitjana", "baixa")
EQUIP = ("marta", "luis", "nuria")
OPCIONS = ("1", "2", "3", "4", "5", "6")
ORDRE_PRIORITAT = {"alta": 0, "mitjana": 1, "baixa": 2}
# --- Sense canvis: demanar_text, demanar_opcio, demanar_enter, confirmar,
# classificar_urgencia, mostrar_menu (ara amb 6 opcions) i pausar ---
def registrar_tasca(agenda):
"""Demana una tasca nova i l'afegeix al final de l'agenda."""
agenda.append({
"titol": demanar_text("Titol : "),
"responsable": demanar_opcio("Responsable : ", EQUIP).capitalize(),
"prioritat": demanar_opcio("Prioritat : ", PRIORITATS),
"dies": demanar_enter("Dies (1-365) : ", 1, 365),
"completada": False})
print(f"Tasca {len(agenda)} registrada correctament.")
def mostrar_llistat(agenda):
"""Pinta l'agenda ordenada per prioritat, amb el numero real de cada tasca."""
if not agenda:
print("No hi ha cap tasca registrada.")
return False
parells = sorted(enumerate(agenda, start=1),
key=lambda p: ORDRE_PRIORITAT[p[1]["prioritat"]])
print("-" * AMPLE)
for numero, tasca in parells:
marca = "[X]" if tasca["completada"] else "[ ]"
print(f"{numero:>2}. {marca} {tasca['titol']:<24}"
f"{tasca['responsable']:>8}{tasca['prioritat']:>7}{tasca['dies']:>4}d")
fetes = sum(1 for t in agenda if t["completada"])
print(f"Total: {len(agenda)} tasques, {fetes} completades, "
f"{sum(t['dies'] for t in agenda)} dies de feina.")
return True
def triar_tasca(agenda):
"""Retorna la tasca que tria l'usuari, o None si l'agenda esta buida."""
if not mostrar_llistat(agenda):
return None
numero = demanar_enter(f"Numero de tasca (1-{len(agenda)}): ", 1, len(agenda))
return agenda[numero - 1]
# canviar_prioritat(tasca) i marcar_completada(tasca) es queden tal com estaven
# a la v0.9: ja rebien un diccionari i el modificaven al lloc.
def resum_per_responsable(agenda):
"""Pinta quantes tasques i quants dies acumula cada persona de l'equip."""
resum = {}
for tasca in agenda:
dades = resum.setdefault(tasca["responsable"], {"tasques": 0, "dies": 0})
dades["tasques"] += 1
dades["dies"] += tasca["dies"]
for responsable, dades in sorted(resum.items(), key=lambda p: -p[1]["dies"]):
print(f"{responsable:<12}{dades['tasques']:>3} tasques{dades['dies']:>5} dies")
sense_tasques = {n.capitalize() for n in EQUIP} - set(resum)
if sense_tasques:
print("Sense tasques assignades:", ", ".join(sorted(sense_tasques)))
def main():
"""Executa el bucle principal de l'aplicacio."""
agenda = []
while True:
mostrar_menu()
opcio = demanar_opcio("Tria una opcio (1-6): ", OPCIONS)
if opcio == "6":
if confirmar("Segur que vols sortir?"):
break
elif opcio == "1":
registrar_tasca(agenda)
elif opcio == "2":
mostrar_llistat(agenda)
elif opcio == "3":
tasca = triar_tasca(agenda)
if tasca is not None:
canviar_prioritat(tasca)
elif opcio == "4":
tasca = triar_tasca(agenda)
if tasca is not None:
marcar_completada(tasca)
elif opcio == "5":
resum_per_responsable(agenda)
pausar()
if __name__ == "__main__":
main()Els punts que convé entendre bé:
triar_tascaretorna el diccionari, no un índex. I com que el diccionari és mutable i es passa per referència,canviar_prioritat(tasca)modifica la tasca dins de l'agenda sense necessitat de saber en quina posició era. Compara això amb els cincpopcoordinats de la v0.8.sorted(enumerate(agenda, start=1), key=lambda p: ORDRE_PRIORITAT[p[1]["prioritat"]])ordena per l'ordre lògic de les prioritats —fent servir un diccionari com a taula de traducció— sense perdre el número real de cada tasca:enumerateprodueix parells(numero, tasca)i elkeymira dins del segon element del parell. Així el llistat surt ordenat però els números continuen servint per triar, itriar_tascapot reutilitzarmostrar_llistaten lloc de repetir el bucle (DRY, 04-04).sum(1 for t in agenda if t["completada"])compta les completades sense bucle explícit, isum(t['dies'] for t in agenda)acumula els dies: comprensions de 05-01 usades dins desum.resum.setdefault(responsable, {...})crea la fitxa del responsable la primera vegada que apareix i la retorna les següents: el patró de recompte de 05-03, aquí construint un diccionari de diccionaris sobre la marxa. I{n.capitalize() for n in EQUIP} - set(resum)és un conjunt per comprensió menys un altre conjunt: qui de l'equip no té res assignat, en una línia. Finalment,if not agenda:aprofita que una llista buida és falsa (02-04) i es llegeix millor quelen(agenda) == 0.
Amb això TascaFàcil fa per fi el que la Marta necessita, i queda un sol cap solt, el que dóna títol a la pròxima lliçó: en tancar el programa, tot desapareix.
Errors Comuns i Consells
Oblidar la coma a la tupla d'un element. ("Marta") és una cadena; ("Marta",) és una tupla. Es nota quan len() retorna 5 en comptes d'1. I modificar una tupla dóna TypeError: si necessites canviar valors, fes servir una llista.
Desempaquetar amb un nombre de noms diferent. a, b = (1, 2, 3) llança ValueError; fes servir a, *resta = ... si no saps quants elements vindran. Confondre els nivells en una expressió encadenada. agenda["titol"] falla perquè agenda és una llista: primer cal triar un element (agenda[0]) i després la clau. Si et perds, imprimeix els passos intermedis. I copiar en superfície una estructura imbricada no protegeix els diccionaris de dins: fes servir copy.deepcopy quan vulguis un arbre independent.
Consell: mantén homogènia la llista de diccionaris. Que tots tinguin les mateixes claus és el que permet recórrer-la sense comprovacions; si un registre pot no tenir un camp, dóna-li un valor per defecte en crear-lo en lloc d'ometre la clau. I no imbriquis més de tres nivells: dades["a"][0]["b"][2]["c"] és indesxifrable i fràgil. Extreu subestructures a variables amb nom (tasca = agenda[i]) o replanteja el disseny; a partir de certa complexitat, el que demana el problema són classes.
Exercicis
Exercici 1: Predir la sortida
Digues què imprimeix cada línia i quins errors es produeixen.
t = ("Cartell fira", "Marta", 3)
titol, responsable, dies = t
print(dies, t[-1], len(("Marta",)), len(("Marta")))
a, b = 1, 2
a, b = b, a
print(a, b)
primer, *resta = ["Marta", "Luis", "Nuria"]
print(primer, resta)
agenda = [{"titol": "Cartell", "dies": 3}, {"titol": "Menu", "dies": 5}]
copia = agenda.copy()
copia[0]["dies"] = 99
print(agenda[0]["dies"])
print(agenda["titol"])Exercici 2: Consultes sobre l'agenda
Amb l'agenda de sota, escriu les expressions o el codi necessaris per obtenir: els títols de les tasques de prioritat alta; el total de dies pendents (els de les tasques no completades); la tasca amb més dies; la llista de títols ordenada alfabèticament ignorant majúscules; i un diccionari {responsable: nombre_de_tasques}.
agenda = [
{"titol": "Cartell fira", "responsable": "Marta", "prioritat": "alta",
"dies": 3, "completada": False},
{"titol": "menu Sole", "responsable": "Luis", "prioritat": "mitjana",
"dies": 5, "completada": True},
{"titol": "Logotip Vidal", "responsable": "Marta", "prioritat": "alta",
"dies": 2, "completada": False}]Exercici 3: Agrupar per responsable
Escriu agrupar_per_responsable(agenda) que retorni un diccionari de llistes amb els títols de cada persona, incloent-hi els membres d'EQUIP que no tinguin cap tasca (amb llista buida). Després escriu pintar_grups(grups) que ho imprimeixi amb els responsables ordenats per nombre de tasques descendent i els títols alfabèticament.
Solucions
Solució 1.
("Marta",) té un element; ("Marta") és la cadena "Marta", la longitud de la qual és 5. L'intercanvi deixa a = 2 i b = 1 perquè el costat dret s'avalua sencer abans d'assignar. *resta recull el que sobra en una llista. copia és una còpia superficial: els seus elements són els mateixos diccionaris, així que el 99 es veu a l'original. I l'última línia falla perquè agenda és una llista i "titol" no és un índex: faltava baixar un nivell amb agenda[0]["titol"].
Solució 2.
altes = [t["titol"] for t in agenda if t["prioritat"] == "alta"]
pendents = sum(t["dies"] for t in agenda if not t["completada"])
mes_llarga = max(agenda, key=lambda t: t["dies"])
alfabetic = sorted([t["titol"] for t in agenda], key=str.lower)
recompte = {}
for t in agenda:
recompte[t["responsable"]] = recompte.get(t["responsable"], 0) + 1
print(altes, pendents) # ['Cartell fira', 'Logotip Vidal'] 5
print(mes_llarga["titol"]) # menu Sole
print(alfabetic) # ['Cartell fira', 'Logotip Vidal', 'menu Sole']
print(recompte) # {'Marta': 2, 'Luis': 1}Dos detalls. max(agenda, key=...) retorna el diccionari sencer, no els dies, per això cal ["titol"] per veure'n el nom. I el key=str.lower de l'ordre alfabètic és el de 05-02: sense ell, «menu Sole» aniria primer per començar en minúscula.
Solució 3.
EQUIP = ("Marta", "Luis", "Nuria")
def agrupar_per_responsable(agenda): # EQUIP capitalitzat per a aquest exercici
"""Retorna {responsable: [titols]} incloent-hi qui no te tasques."""
grups = {nom: [] for nom in EQUIP} # diccionari per comprensio
for tasca in agenda:
grups.setdefault(tasca["responsable"], []).append(tasca["titol"])
return grups
def pintar_grups(grups):
"""Pinta cada responsable amb les seves tasques ordenades."""
for responsable, titols in sorted(grups.items(), key=lambda p: -len(p[1])):
print(f"{responsable} ({len(titols)}):")
for titol in sorted(titols, key=str.lower):
print(f" - {titol}")
pintar_grups(agrupar_per_responsable(agenda))El diccionari per comprensió inicial garanteix que tots els membres de l'equip apareguin, encara que no tinguin tasques: sense ell, la Nuria es quedaria fora de l'informe. El setdefault que ve després cobreix el cas contrari —un responsable que no sigui a EQUIP—, així que la funció no es trenca amb dades inesperades. I key=lambda p: -len(p[1]) ordena de més a menys fent servir el truc del signe negatiu, alternativa a reverse=True quan el criteri és numèric.
Conclusió
Una tupla és una seqüència immutable: el que la crea són les comes, la d'un element necessita la coma final (x,), i el seu paper natural és el registre de camps fixos, la constant i el valor retornat. El desempaquetatge reparteix els seus elements en variables (titol, responsable, dies = tasca), permet l'intercanvi en una línia i admet *resta per recollir el que sobra. Amb això queda tancada la promesa de 04-02: retornar diversos valors és retornar una tupla, i una funció sempre retorna un únic objecte. En ser immutables, les tuples serveixen com a claus de diccionari per indexar per combinacions, i namedtuple existeix per si te la trobes.
Les estructures imbricades es redueixen a la pràctica a tres patrons: llista de diccionaris per a molts registres, diccionari de llistes per agrupar i diccionari de diccionaris per a fitxes indexades. Es llegeixen d'esquerra a dreta baixant un nivell per cada claudàtor, es recorren amb bucles imbricats i desempaquetatge al for, i amaguen un parany: la còpia superficial no duplica l'interior, per a això hi ha copy.deepcopy. TascaFàcil ha assolit la v0.10, i és un programa de debò: l'agenda és una llista de diccionaris, el llistat surt numerat i ordenat per prioritat, triar_tasca retorna la tasca que després es modifica al lloc, i un resum agrupa la feina per responsable assenyalant qui està lliure. Les llistes paral·leles de la v0.8 i els seus riscos de desincronització han desaparegut del tot. Però continua havent-hi un final infeliç: quan la Marta tanca el programa, l'agenda sencera s'evapora. Tota aquesta estructura viu només a la memòria. A Desar dades en fitxers: text, CSV i JSON aprendràs a escriure-la al disc i a recuperar-la en arrencar, de manera que la feina de l'estudi continuï allà demà al matí. És l'últim cap solt del mòdul.
Fonaments de la Programació
Mòdul 1: Introducció a la Programació
- Què és la programació?
- Història de la programació
- Llenguatges de programació
- Entorns de desenvolupament
- Del problema a l'algorisme
Mòdul 2: Conceptes Bàsics
- Variables i tipus de dades
- Operadors i expressions
- Entrada i sortida de dades
- Conversió de tipus i validació de dades
Mòdul 3: Estructures de Control
Mòdul 4: Funcions i Procediments
- Definició i ús de funcions
- Paràmetres i retorn de valors
- Àmbit de variables
- Descompondre un programa en funcions
- Funcions com a valors: lambda i ordre superior
Mòdul 5: Estructures de Dades
- Llistes i arrays
- Cadenes de caràcters
- Diccionaris i conjunts
- Tuples i estructures imbricades
- Desar dades en fitxers: text, CSV i JSON
Mòdul 6: Algorismes Bàsics
Mòdul 7: Objectes i Organització del Codi
- De les dades als objectes: classes i instàncies
- Atributs, mètodes i constructor
- Col·leccions d'objectes
- Mòduls, paquets i importacions
Mòdul 8: Bones Pràctiques i Eines
- Documentació i comentaris
- Depuració i gestió d'errors
- Control de versions
- Proves automatitzades
- Estil, llegibilitat i refactorització
