Les tres funcions que vam escriure a la lliçó anterior —mostrar_capcalera(), mostrar_menu() i pausar()— tenen una limitació evident: fan sempre exactament el mateix. mostrar_menu() pinta aquell menú i no cap altre; pausar() mostra aquell missatge i no cap altre. Són útils, però rígides. I per això mateix no ens han servit per al problema de fons de TascaFàcil: els quatre bucles de validació repetits, que s'assemblen entre si però no són idèntics —cadascun té el seu missatge, els seus valors acceptables i el seu tipus de dada.
Per extreure això calen les dues peces que falten. La primera, dir coses a la funció quan la crides: això són els paràmetres, i converteixen una funció que fa una cosa en una funció que fa tota una família de coses. La segona, que la funció et lliuri un resultat: això és return, i és el que permet fer servir el valor calculat a qualsevol lloc, desar-lo en una variable o passar-lo a una altra funció.
Amb paràmetres i return, una funció deixa de ser una drecera per escriure menys i es converteix en una peça de construcció: alguna cosa amb entrades i sortides ben definides que pots muntar amb altres peces. Aquesta és la lliçó més important del mòdul.
Contingut
- Paràmetres i arguments: dues paraules per a dues coses
- Arguments posicionals i arguments per nom
- Valors per defecte
return: retornar un valor i acabar- Diversos
returni elreturnprimerenc - Retornar més d'un valor
- Imprimir a dins o retornar?
*argsi**kwargs, per reconèixer-los- TascaFàcil: les funcions de validació
- Errors comuns i consells
- Exercicis
- Conclusió
- Paràmetres i arguments: dues paraules per a dues coses
Un paràmetre és el nom que apareix entre els parèntesis de la definició; un argument és el valor concret que es lliura entre els parèntesis de la crida.
def saludar(nom): # 'nom' es el PARAMETRE
print(f"Hola, {nom}. Benvinguda a Estudi Alba.")
saludar("Marta") # "Marta" es l'ARGUMENT
saludar("Luis")El que passa a la crida és exactament això: Python crea, dins de la funció, una variable anomenada nom i li assigna el valor "Marta" —una assignació normal i corrent, nom = "Marta", feta automàticament per tu. Aquesta variable viu només mentre la funció s'executa i desapareix en acabar; el perquè exacte és el tema d'Àmbit de variables.
| Paràmetre | Argument | |
|---|---|---|
| On apareix | A la definició (def) |
A la crida |
| Què és | Un nom, un buit per omplir | Un valor concret |
| Quants n'hi ha | Fixos en escriure la funció | Un per cada paràmetre, a cada crida |
| Exemple | def saludar(nom): |
saludar("Nuria") |
Distingir-los importa perquè els missatges d'error de Python parlen en aquests termes: cridar saludar() sense res produeix TypeError: saludar() missing 1 required positional argument: 'nom', és a dir, «falta 1 argument obligatori i es diu nom». Si en passes de més, dirà takes 1 positional argument but 2 were given. Tan bon punt entens el vocabulari, aquests errors deixen de fer por i passen a ser instruccions força precises.
Una funció pot tenir diversos paràmetres, separats per comes, i llavors l'ordre mana:
def presentar_tasca(titol, responsable, dies):
print(f"{titol} -> {responsable} ({dies} dies)")
presentar_tasca("Cartell fira del llibre", "Nuria", 4)
presentar_tasca("Nuria", 4, "Cartell fira del llibre") # valid, encara que absurdLa segona crida no dona error: Python assigna per posició sense jutjar si té sentit. És una fallada silenciosa, de les pitjors, i la secció següent n'ofereix la defensa.
- Arguments posicionals i arguments per nom
Fins ara hem passat arguments posicionals: el primer valor va al primer paràmetre, el segon al segon, i així. Però Python permet també anomenar el paràmetre al qual va cada valor. S'anomenen arguments per nom o keyword arguments:
presentar_tasca(titol="Menu del Forn Sole", responsable="Luis", dies=3)
presentar_tasca(responsable="Luis", dies=3, titol="Menu del Forn Sole")Les dues crides fan exactament el mateix: en indicar el nom, l'ordre deixa d'importar. I de passada, la crida es llegeix sola. Compara crear_avis("Cartell fira", True, False, 3) amb crear_avis("Cartell fira", urgent=True, silencios=False, dies=3): sense els noms, per entendre la primera hauries d'anar a buscar la definició de crear_avis; amb ells, la crida s'explica al lloc on és.
Es poden barrejar els dos estils, amb una regla ferma: els posicionals van sempre abans que els nomenats.
presentar_tasca("Cartell fira", responsable="Nuria", dies=4) # correcte
presentar_tasca(titol="Cartell fira", "Nuria", 4) # SyntaxErrorCriteri pràctic: fes servir arguments per nom quan el valor sigui un True/False solt, un número sense unitats evidents o quan la funció tingui més de dos o tres paràmetres; un True despullat en una crida és una endevinalla per a qui llegeixi el codi d'aquí a un mes.
- Valors per defecte
Un paràmetre pot portar un valor per defecte, que es fa servir si qui crida no proporciona aquell argument. S'escriu amb un = a la definició:
def avisar(missatge, vegades=1):
"""Repeteix un avis per pantalla el nombre de vegades indicat."""
for i in range(vegades):
print(f"[AVIS] {missatge}")
avisar("Revisar l'encarrec del client Vidal") # imprimeix 1 linia
avisar("Lliurament dema", vegades=3) # imprimeix 3 liniesvegades s'ha tornat opcional: la primera crida fa servir el valor per defecte, 1, i la segona el sobreescriu. Així s'aconsegueix que una funció sigui senzilla en el cas normal i flexible en l'especial, que és exactament el que vols. Hi ha una regla sintàctica que Python imposa: els paràmetres amb valor per defecte van al final, després de tots els obligatoris. És a dir, def demanar_text(missatge, obligatori=True): és correcte, però def demanar_text(obligatori=True, missatge): produeix SyntaxError: parameter without a default follows parameter with a default. La raó és de sentit comú: si els opcionals poguessin anar al mig, Python no sabria a quin paràmetre correspon el segon valor d'una crida posicional.
Un avís que convé gravar des d'avui, encara que la seva explicació completa arribi més endavant: no facis servir com a valor per defecte un objecte mutable (una llista, per exemple). El valor per defecte s'avalua una sola vegada, en definir la funció, no a cada crida; si és mutable i es modifica a dins, aquesta modificació persisteix entre crides i produeix un comportament desconcertant. Amb valors immutables —números, cadenes, booleans, None— no hi ha cap problema, i és l'únic que farem servir de moment. Hi tornarem quan coneguis les llistes.
return: retornar un valor i acabar
return: retornar un valor i acabarreturn fa dues coses alhora, i totes dues importen:
- Lliura un valor a qui ha cridat la funció.
- Acaba la funció immediatament: cap línia posterior del cos no s'executa.
def calcular_cost(dies, tarifa_diaria):
"""Retorna el cost total d'una tasca, sense imprimir res."""
return dies * tarifa_diaria
cost = calcular_cost(4, 120.0)
print(f"Cost estimat: {cost:.2f} EUR")
print(f"Amb IVA: {cost * 1.21:.2f} EUR")Fixa't en el que ha guanyat el programa. calcular_cost no sap ni li importa què es farà amb el número: el lliura i s'aparta. Qui el crida el desa a cost, el formata i el reutilitza per a l'IVA. Una crida a una funció que retorna valor és una expressió, amb tot el que això significa: pot anar dins d'un print, d'un if, d'una operació aritmètica o dins d'una altra crida.
print(calcular_cost(2, 90.0)) # dins de print
if calcular_cost(10, 120.0) > 1000: # dins d'una condicio
print("Pressupost alt: requereix aprovacio de la Marta.")
total = calcular_cost(3, 100.0) + calcular_cost(2, 80.0) # en una sumaI la segona meitat de return, la d'acabar, es veu així: si escrius return 42 i a sota un print("..."), aquest print és codi mort i no s'executa mai; els editors solen marcar-lo en gris. Si te'n surt un, o sobra o el return és al lloc equivocat.
- Diversos
return i el return primerenc
return i el return primerencUna funció pot tenir tants return com calgui. S'executa el primer que s'assoleixi, i allà s'acaba tot:
def etiqueta_de_carrega(dies):
"""Retorna una etiqueta de carrega de treball segons els dies estimats."""
if dies <= 2:
return "lleugera"
if dies <= 5:
return "mitjana"
return "pesada"
print(etiqueta_de_carrega(1)) # lleugera
print(etiqueta_de_carrega(4)) # mitjana
print(etiqueta_de_carrega(9)) # pesadaFixa't que no hi ha ni elif ni else, i tanmateix funciona: com que cada branca acaba en return, si la primera condició es compleix la funció ja ha acabat i mai no arriba a la segona. Aquesta és la clàusula de guarda de 03-01 portada a les funcions: comprovar primer els casos particulars, sortir tan bon punt es resolen i deixar el cas general al final, sense indentar. Es llegeix molt millor que un if imbricat de tres nivells.
Un return sense valor —només la paraula— també existeix: acaba la funció i retorna None. És útil per abandonar aviat un procediment:
def mostrar_fitxa(hi_ha_tasca, titol):
if not hi_ha_tasca:
print("Encara no hi ha cap tasca registrada.")
return # sortim: no hi ha res a pintar
print(f"Fitxa de: {titol}")Compte amb un descuit habitual: si una branca oblida el seu return, aquesta branca retorna None en silenci, i la funció sembla correcta fins que algú la crida amb el valor que hi cau just. Comprova una per una que totes les branques retornen alguna cosa.
- Retornar més d'un valor
Si separes diversos valors amb comes després de return, la funció els retorna tots alhora:
def analitzar_titol(text):
"""Retorna el titol normalitzat i el seu nombre de caracters."""
net = text.strip().capitalize()
return net, len(net)
titol, longitud = analitzar_titol(" cartell fira del llibre ")
print(titol) # Cartell fira del llibre
print(longitud) # 23El que passa per dins és que Python empaqueta els dos valors en un únic objecte anomenat tupla, i la línia titol, longitud = ... el desempaqueta en dues variables. Pots comprovar-ho desant el resultat en una sola variable: resultat = analitzar_titol("cartell fira") deixa a resultat el valor ('Cartell fira', 12), el type() del qual és <class 'tuple'>. Les tuples són el tema de Tuples i estructures imbricades; avui n'hi ha prou de saber que el desempaquetatge funciona i que el nombre de variables de l'esquerra ha de coincidir amb el de valors retornats, o tindràs un ValueError: too many values to unpack.
Un consell de disseny: retornar dos valors està bé; retornar-ne cinc és senyal que aquella funció fa massa coses, o que aquests cinc valors són en realitat un sol concepte que encara no té nom —just el que li passa a TascaFàcil amb titol, responsable, prioritat, dies i completada, i que es resoldrà als mòduls 5 i 7.
- Imprimir a dins o retornar?
Aquesta és la decisió de disseny més freqüent en escriure una funció, i la resposta correcta gairebé sempre és la mateixa. Compara una cost_imprimit(dies, tarifa) el cos de la qual és print(f"Cost: {dies * tarifa:.2f} EUR") amb una cost_retornat(dies, tarifa) el cos de la qual és return dies * tarifa:
| Criteri | Imprimir a dins | Retornar el valor |
|---|---|---|
| Es pot reutilitzar el resultat? | No: se n'ha anat a la pantalla | Sí: és un valor com qualsevol altre |
| Serveix per calcular una altra cosa? | No | Sí (total = a() + b()) |
| Es pot provar sense mirar la pantalla? | No | Sí: es compara el valor retornat |
| Serveix per a un informe, un fitxer, una altra pantalla? | No: el format està fixat | Sí: ho decideix qui crida |
| Qui decideix el format? | La funció, per sempre | Qui la fa servir, en cada cas |
Retornar és gairebé sempre millor, i la raó de fons és que una funció que imprimeix ha pres per tu una decisió que no li tocava. cost_imprimit només serveix per mostrar euros amb dos decimals a la pantalla; el dia que vulguis el cost per sumar-lo, comparar-lo o desar-lo, no et val. cost_retornat serveix per a tot això i també per imprimir, perquè sempre pots fer print(f"{cost_retornat(4, 120):.2f} EUR"). La conseqüència pràctica és una regla que reprendrem a Descompondre un programa en funcions: les funcions que calculen no imprimeixen; les que presenten no calculen. I la que sí que ha d'imprimir és precisament la que dialoga amb l'usuari, com demanar_text, que necessita mostrar el missatge per poder preguntar.
*args i **kwargs, per reconèixer-los
*args i **kwargs, per reconèixer-losVeuràs aquestes dues formes pertot arreu en el codi aliè, així que convé reconèixer-les encara que no les facis servir encara.
Un paràmetre precedit d'un asterisc recull tots els arguments posicionals sobrants, i un de precedit de dos asteriscos recull tots els arguments per nom sobrants:
def anunciar(*missatges):
"""Imprimeix per pantalla tants missatges com se li passin."""
for missatge in missatges:
print(f"[AVIS] {missatge}")
anunciar("Reunio a les 10", "Lliurar el cartell", "Trucar a Vidal")La versió amb dos asteriscos es defineix igual —def registrar_esdeveniment(accio, **detalls):— i es crida amb registrar_esdeveniment("alta_tasca", responsable="Nuria", dies=4); a dins, detalls associa cada nom amb el seu valor.
Els noms args i kwargs són pura convenció (d'arguments i keyword arguments); el que fa la feina són els asteriscos. Per dins, *args és una tupla i **kwargs un diccionari, dues estructures del mòdul 5: no cal que les dominis avui, només reconèixer que def funcio(*args, **kwargs): accepta qualsevol cosa. Fes-ne un ús moderat: una funció amb paràmetres explícits es documenta i s'entén molt millor.
- TascaFàcil: les funcions de validació
Ha arribat el moment de matar la repetició. Els quatre bucles de validació de la v0.6 es redueixen a tres funcions parametritzades, més una que classifica.
def demanar_text(missatge, obligatori=True):
"""Demana un text per teclat i el retorna ja net d'espais."""
valor = input(missatge).strip()
while obligatori and valor == "":
valor = input("No pot estar buit. " + missatge).strip()
return valor
def demanar_opcio(missatge, opcions):
"""Demana un valor fins que sigui dins de les opcions admeses."""
valor = input(missatge).strip().lower()
while valor not in opcions:
valor = input(f"No valid. Admesos: {', '.join(opcions)}. ").strip().lower()
return valor
def demanar_enter(missatge, minim, maxim):
"""Demana un enter i no el retorna fins que sigui entre minim i maxim."""
text = input(missatge).strip()
while not text.isdigit() or not minim <= int(text) <= maxim:
text = input(f"Ha de ser un enter entre {minim} i {maxim}. ").strip()
return int(text)
def classificar_urgencia(prioritat, dies):
"""Retorna l'etiqueta d'urgencia que correspon a la tasca."""
if prioritat == "alta" and dies <= 2:
return "CRITICA"
if prioritat == "alta":
return "Urgent"
if prioritat == "mitjana" and dies <= 3:
return "Atencio"
return "Normal"I així es fan servir des del programa principal:
PRIORITATS = ("alta", "mitjana", "baixa")
EQUIP = ("marta", "luis", "nuria")
titol = demanar_text("Titol : ")
responsable = demanar_opcio("Responsable : ", EQUIP).capitalize()
prioritat = demanar_opcio("Prioritat : ", PRIORITATS)
dies = demanar_enter("Dies (1-365) : ", 1, 365)
print(f"{titol} [{classificar_urgencia(prioritat, dies)}]")Repassa el que ha passat, perquè és molt:
- Quatre bucles de validació repetits han quedat en tres funcions, cadascuna escrita una sola vegada. Si demà vols canviar el missatge de retret, el canvies en un lloc i canvia a tots.
- El programa principal es llegeix com una llista d'intencions. La mecànica del
whileha desaparegut de la vista sense desaparèixer del programa. demanar_opcioserveix per al responsable, per a la prioritat i per a l'opció del menú, tres coses que a la v0.6 eren tres blocs diferents. Aquest és el poder d'un paràmetre:opcionsconverteix una funció en una família de funcions. Iobligatori=Truedeixa oberta la porta a demanar una dada opcional el dia que calgui, sense tocar cap crida actual.classificar_urgenciaretorna, no imprimeix. Per això la podem ficar dins d'una f-string, i demà la podríem fer servir també per decidir un color o per ordenar tasques.
Fixa't a més en un detall de repartiment de responsabilitats: demanar_opcio normalitza a minúscules, però el .capitalize() del responsable s'aplica fora, a qui crida. La funció s'ocupa de validar; el format de presentació és assumpte del programa. I observa que demanar_enter retorna ja un int, no un text: converteix una vegada, a l'únic lloc on consta que la dada és convertible, aplicant el marc «presència, tipus, domini» de 02-04 de cap a peus.
Errors Comuns i Consells
Oblidar el return. La funció calcula bé i retorna None. Símptoma: TypeError: unsupported operand type(s) for +: 'NoneType' and 'int' una mica més endavant, a la línia que intenta fer servir el resultat.
Confondre print amb return. Escriure print(dies * tarifa) dins de la funció i després cost = calcular_cost(...) deixa cost a None, encara que el número correcte hagi sortit per pantalla. Veure el valor a la consola no és el mateix que tenir-lo.
Nombre d'arguments incorrecte. TypeError: ... missing 1 required positional argument o takes 2 positional arguments but 3 were given. El missatge diu quants n'esperava i quants n'ha rebut: compta'ls a la definició.
Posar un paràmetre amb defecte abans d'un d'obligatori. SyntaxError immediat: els opcionals van al final, sempre. I no facis servir una llista com a valor per defecte: def f(x, historial=[]) comparteix la mateixa llista entre totes les crides. Encara no manegues llistes; recorda-ho per quan arribi 05-01.
Posar codi després del return. No s'executa mai. Si l'has escrit, és que volies una altra cosa.
Consell: escriu primer la crida. Abans d'implementar, escriu com t'agradaria fer-la servir: dies = demanar_enter("Dies: ", 1, 365). Si aquesta línia es llegeix bé, la signatura és bona; si t'obliga a explicar-la, canvia-la abans d'escriure el cos.
Consell: dos o tres paràmetres és el còmode. A partir de quatre, la crida es torna un jeroglífic i convé fer servir arguments per nom —o preguntar-se si aquella funció no estarà fent dues coses.
Exercicis
Exercici 1: Predir i explicar
Digues què imprimeix cada bloc i per què. Hi ha una errada de disseny amagada.
def mitjana_dies(a, b):
print((a + b) / 2)
def mitjana_dies_2(a, b):
return (a + b) / 2
x = mitjana_dies(4, 6)
y = mitjana_dies_2(4, 6)
print(x)
print(y)
print(y * 2)Exercici 2: Pressupost de l'estudi
Escriu tres funcions amb docstring d'una línia:
cost_tasca(dies, tarifa=110.0): retorna el cost d'una tasca; la tarifa per defecte és 110 euros al dia.aplicar_descompte(cost, percentatge=0): retorna el cost amb el descompte aplicat. Si el percentatge no és entre 0 i 50, retorna el cost sense tocar.resum_pressupost(titol, dies, tarifa=110.0, descompte=0): fa servir les dues anteriors i retorna dos valors: el text de resum i l'import final.
Després, calcula el pressupost de «Cartell fira del llibre», 4 dies, tarifa per defecte i 10 % de descompte, i imprimeix el resum des del programa principal (no des de dins de les funcions).
Exercici 3: Reparar la funció
Aquesta funció té tres defectes. Troba'ls, explica'ls i reescriu-la.
def classificar(dies, prioritat="alta", llindar):
if prioritat == "alta":
print("Urgent")
elif dies > 10:
return "Llarg termini"
return "Normal"
print("Classificacio acabada")Solucions
Solució 1.
mitjana_dies(4, 6) imprimeix 5.0 des de dins i retorna None, així que x val None. mitjana_dies_2(4, 6) no imprimeix res però retorna 5.0, que es desa a y. Després print(x) mostra None, print(y) mostra 5.0 i print(y * 2) mostra 10.0. L'errada de disseny és mitjana_dies: en imprimir en comptes de retornar, el seu resultat no es pot reutilitzar. Comprova que x * 2 donaria TypeError, mentre que y * 2 funciona sense problema.
Solució 2.
def cost_tasca(dies, tarifa=110.0):
"""Retorna el cost brut d'una tasca de la duracio indicada."""
return dies * tarifa
def aplicar_descompte(cost, percentatge=0):
"""Retorna el cost amb descompte; ignora percentatges fora de 0-50."""
if not 0 <= percentatge <= 50:
return cost
return cost * (1 - percentatge / 100)
def resum_pressupost(titol, dies, tarifa=110.0, descompte=0):
"""Retorna el text de resum i l'import final del pressupost."""
brut = cost_tasca(dies, tarifa)
final = aplicar_descompte(brut, descompte)
text = f"{titol}: {dies} dies x {tarifa:.2f} = {brut:.2f} EUR"
return text, final
detall, import_final = resum_pressupost("Cartell fira del llibre", 4, descompte=10)
print(detall) # ... 4 dies x 110.00 = 440.00 EUR
print(f"Total amb descompte: {import_final:.2f} EUR") # Total amb descompte: 396.00 EURTres detalls: descompte=10 es passa per nom per saltar-se tarifa sense repetir el seu valor per defecte; cap de les tres funcions no imprimeix, així que totes tres són reutilitzables; i la comparació encadenada 0 <= percentatge <= 50 és la de 03-01, dins d'una guarda amb return primerenc.
Solució 3.
| Defecte | Explicació |
|---|---|
llindar sense defecte va darrere de prioritat="alta" |
SyntaxError en carregar el fitxer |
| La primera branca imprimeix en comptes de retornar | La funció retorna None quan la prioritat és alta |
El print final és darrere d'un return |
Codi mort: no s'executa mai |
def classificar(dies, llindar, prioritat="alta"):
"""Retorna la classificacio d'una tasca segons prioritat i duracio."""
if prioritat == "alta":
return "Urgent"
if dies > llindar:
return "Llarg termini"
return "Normal"El paràmetre obligatori passa davant, la primera branca retorna com les altres, el codi mort desapareix i de passada llindar es fa servir de debò, que a l'original ni tan sols apareixia al cos.
Conclusió
Les teves funcions ja dialoguen. Els paràmetres són els buits que declares al def; els arguments són els valors que lliures a la crida, i poden anar per posició o per nom, sent aquesta segona forma la que fa llegibles les crides amb booleans o amb moltes dades. Un paràmetre pot portar valor per defecte, i llavors es torna opcional, amb la regla que els opcionals van al final i mai no són mutables. De l'altra banda, return lliura un valor i acaba la funció a l'acte; n'hi pot haver diversos en branques diferents, serveix com a clàusula de guarda per sortir aviat, i admet diversos valors separats per comes que es desempaqueten en rebre'ls. I la regla que ordena tota la resta: una funció que calcula retorna, no imprimeix, perquè el valor serveix per a tot i la pantalla només per mirar.
TascaFàcil ha fet el salt més gran de la seva història. Els quatre bucles de validació duplicats s'han convertit en demanar_text, demanar_opcio i demanar_enter, tres funcions que s'escriuen una vegada i es fan servir des de qualsevol lloc, i classificar_urgencia calcula una etiqueta sense embrutar la pantalla. El programa principal ha passat de ser un embolic de while a una llista d'intencions llegible. Però en reunir totes aquestes funcions en un fitxer apareixen preguntes noves i molt concretes. Per què demanar_opcio pot llegir la constant PRIORITATS sense que la hi passem? Què passaria si dins d'una funció hi assignés titol = "un altre": canviaria el titol del programa principal? I per què la variable valor de demanar_text no existeix fora? Tot això és una sola qüestió —on viu cada nom— i té nom propi: l'àmbit. És la lliçó vinent: Àmbit de variables.
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ó
