Amb la v0.18 el paquet tascafacil/ s'entén: té docstrings, anotacions de tipus i un README. Però continua sent fràgil. Si la Marta obre el tasques.json amb un editor i s'hi deixa una coma de més, el programa mor amb un traceback abans de mostrar el menú. Si en Luis escriu «tres» quan se li demanen els dies, mor igualment. I tot el que hi hagués a la memòria es perd. Aquesta lliçó tanca per fi la promesa que el curs arrossega des de Conversió de tipus i validació i des de Desar dades en fitxers: capturar els errors amb try/except en lloc de pregar perquè no passin. Aprendràs a distingir els tres tipus d'error, a llegir un traceback sense por, a decidir què es captura i què es deixa pujar, a llançar excepcions pròpies com TascaInvalida, a registrar el que passa amb logging en lloc de fer-ho amb print i a depurar amb punts d'interrupció en comptes d'endevinar. Al final, TascaFàcil deixarà de trencar-se.
Contingut
- Els tres tipus d'error
- Llegir un traceback
- Catàleg d'excepcions habituals
try/except: capturar el que s'ha de capturarelseifinally: el flux completraisei excepcions pròpies- La política d'errors: EAFP davant de LBYL
loggingdavant deprint- Depurar de debò: mètode, VS Code i
pdb - TascaFàcil v0.19: el programa que no es trenca
- Errors habituals i consells
- Exercicis
- Conclusió
- Els tres tipus d'error
No tots els errors són iguals, i cadascun es caça d'una manera diferent; distingir-los és el primer pas per no perdre el temps buscant on no hi ha res. El tercer és el perillós: els dos primers avisen a crits, però l'error de lògica es queda callat, imprimeix «progrés: 0,6 %» i ningú no se n'adona fins que la Marta pregunta per què la tasca del cartell porta tres setmanes al 0,6 %. Contra aquest no hi ha try/except que valgui: calen el mètode de depuració de l'apartat 9 i les proves de Proves automatitzades.
| Tipus | Quan apareix | Qui el detecta | Com s'arregla |
|---|---|---|---|
| De sintaxi | Abans d'executar res | Python, en llegir el fitxer | Corregint l'escriptura |
| D'execució (excepció) | Durant l'execució | L'intèrpret, en arribar a la línia | Amb validació o try/except |
| De lògica | Mai: el programa «funciona» | Només tu, mirant el resultat | Depurant i provant |
print("Tasques de la Marta" # 1. SyntaxError: no s executa RES del fitxer
dies = int("tres") # 2. ValueError: la sintaxi val, la dada no
def progres(fets, dies): # 3. Error de logica: ningu no protesta...
return fets / dies # ...i hauria de ser fets / dies * 100
- Llegir un traceback
Quan salta una excepció, Python imprimeix un traceback: el mapa de com s'ha arribat fins a l'error. Es llegeix d'una manera molt concreta: de baix a dalt per saber què ha passat, i de dalt a baix per saber per què.
Traceback (most recent call last):
File "/home/marta/projectes/tascafacil/__main__.py", line 34, in main
agenda = magatzem.carregar()
File "/home/marta/projectes/tascafacil/magatzem.py", line 41, in carregar
dades = json.load(f)
File "/usr/lib/python3.12/json/__init__.py", line 293, in load
return loads(f.read(), ...)
json.decoder.JSONDecodeError: Expecting ',' delimiter: line 8 column 3Aquest bloc conté tota la informació que necessites:
most recent call last: les crides estan en ordre —a dalt la primera, a baix la que ha esclatat—; això és la pila de crides. I l'última línia diu què ha passat: el tipus (JSONDecodeError) i el missatge (falta una coma a la línia 8 del fitxer de dades).- La línia culpable del teu codi és l'última que pertany al teu projecte, aquí
magatzem.py, line 41. El que hi ha a sota és la biblioteca estàndard:jsonno falla, només és on es manifesta. - L'origen real sol ser més amunt:
carregar()funciona bé; el problema és el fitxer que se li ha donat. Recorre la pila cap amunt preguntant-te «qui li ha passat aquesta dada?»; en el 90 % dels casos l'error hi és.
- Catàleg d'excepcions habituals
Aquestes són les que et trobaràs una vegada i una altra; reconèixer-les d'un cop d'ull estalvia moltíssim temps:
| Excepció | Causa típica |
|---|---|
ValueError |
El tipus és correcte però el valor no: int("tres") |
TypeError |
Operació entre tipus incompatibles: "5" + 5 |
KeyError |
Clau que no existeix en un diccionari: tasca["data"] |
IndexError |
Índex fora de rang: tasques[10] amb 3 tasques |
FileNotFoundError |
El fitxer no existeix o la ruta és incorrecta |
ZeroDivisionError |
Divisió o mòdul per zero |
AttributeError |
L'objecte no té aquest atribut o mètode: None.titol |
json.JSONDecodeError |
El fitxer JSON està corrupte o buit |
try/except: capturar el que s'ha de capturar
try/except: capturar el que s'ha de capturarLa sintaxi és simple: al bloc try hi va el codi que pot fallar; a l'except, què fer si falla.
entrada = input("Dies estimats: ")
try:
dies = int(entrada) # aquesta linia pot llancar ValueError
except ValueError:
print("Aixo no es un numero enter. Faig servir 1 dia per defecte.")
dies = 1 # i el programa continuaSense el try, un «tres» tombava el programa. Amb ell, l'usuari rep un missatge comprensible i l'aplicació segueix viva. Ara bé, hi ha dues maneres d'escriure-ho malament:
except: # MALAMENT: captura tot, fins i tot el Ctrl+C de l usuari
dies = 1
except Exception: # GAIREBE IGUAL DE MALAMENT: amaga errors que no esperaves
dies = 1except: a seques captura fins i tot un KeyboardInterrupt (el Ctrl+C amb què l'usuari intenta sortir) i un SystemExit, de manera que el teu programa esdevé impossible d'interrompre. except Exception no arriba tan lluny, però continua tapant errors de programació —un nom mal escrit, un AttributeError— i converteix una fallada evident en un comportament estrany impossible de diagnosticar. La regla és: captura l'excepció concreta que saps que pot passar i que saps com gestionar; la resta ha de pujar i fer soroll. Quan hi ha diverses possibilitats s'encadenen except o s'agrupen en una tupla, i amb as es recull l'objecte per llegir-ne el missatge:
try:
with open(ruta, encoding="utf-8") as f:
agenda = Agenda.from_dict(json.load(f))
except FileNotFoundError:
agenda = Agenda() # primera execucio
except (json.JSONDecodeError, KeyError) as error: # dos casos, un bloc
print(f"El fitxer esta malmes ({error}). Es fara servir una agenda buida.")
agenda = Agenda()Els except es comproven en ordre i només s'executa el primer que encaixa: per això les excepcions concretes van sempre abans que les generals.
else i finally: el flux complet
else i finally: el flux completEl bloc try admet dues clàusules més que arrodoneixen l'estructura. else s'executa només si no hi ha hagut excepció, cosa que permet deixar al try únicament la línia perillosa; finally s'executa sempre, hagi fallat o no, fins i tot si el try fa return, i és el lloc de la neteja.
def carregar_json(ruta):
"""Retorna les dades del fitxer, o None si no s ha pogut llegir."""
try:
with open(ruta, encoding="utf-8") as f:
dades = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as error:
print(f"No s ha pogut llegir {ruta}: {error}")
return None
else:
return dades # nomes si tot ha anat be
finally:
print("Fi de l intent de carrega.") # passi el que passiflowchart TD
A[Entra al try] --> B{Hi ha excepcio?}
B -- No --> C[Executa else] --> G[Executa finally]
B -- Si --> D{Coincideix algun except?}
D -- Si --> E[Executa aquest except] --> G
D -- No --> F[L excepcio puja a qui ha cridat] --> G
G --> H[Continua el programa o propaga l error]
Fixa't en la branca de la dreta: si cap except no encaixa, el finally s'executa igualment i després l'excepció continua pujant. Aquesta és la clau del disseny: finally garanteix la neteja sense empassar-se l'error. Per als fitxers, això ja ho resol una cosa que fas servir des de 05-05: with tanca el fitxer automàticament i fa innecessari el finally en aquest cas.
raise i excepcions pròpies
raise i excepcions pròpiesFins ara les excepcions venien de Python. També pots llançar-les tu, i és el correcte quan la teva funció rep alguna cosa amb què no pot treballar: fallar aviat i amb un missatge clar és millor que retornar un valor inventat.
def crear_tasca(titol, dies):
"""Crea una tasca validant les dades d entrada."""
if not titol.strip():
raise ValueError("El titol no pot estar buit.")
if dies <= 0:
raise ValueError(f"Els dies han de ser positius, i han arribat {dies}.")
return Tasca(titol, dies)
try:
tasca = crear_tasca("", 3)
except ValueError:
print("Avis: dades invalides.")
raise # 'raise' a seques rellanca la mateixa excepcioUn raise a seques dins d'un except rellança l'excepció original amb el seu traceback intacte, que és el que vols quan només necessites anotar alguna cosa de passada. I quan converteixes un error de baix nivell en un altre de més significatiu, encadena'ls amb raise ... from: raise TascaInvalida("prioritat desconeguda") from error conserva la causa al traceback. Definir una excepció pròpia és sorprenentment simple, una classe buida que hereta d'una altra excepció:
class TascaInvalida(ValueError):
"""Les dades d una tasca no compleixen les regles de l Estudi Alba."""
class PrioritatInvalida(TascaInvalida): # es poden encadenar en jerarquia
"""La prioritat no es 'alta', 'mitjana' ni 'baixa'."""Per què heretar de ValueError i no d'Exception? Perquè així el codi antic continua funcionant: qui capturava ValueError per validar entrades continuarà capturant també les teves. Recorda a més que totes les excepcions descendeixen d'Exception, i que aquesta jerarquia mana: except ValueError no atrapa un KeyError, però except Exception els atrapa tots, inclosos els que no esperaves. Una jerarquia pròpia dona precisió en capturar (except TascaInvalida atrapa qualsevol problema de dades de tasca, inclosa PrioritatInvalida, sense tocar un ValueError d'una altra banda), missatges del domini en lloc de missatges sobre conversions de text, i un únic lloc que documenta les regles del negoci. La convenció és agrupar-les al mòdul del domini —a TascaFàcil, model.py— amb noms recognoscibles (...Invalida, ...Error).
- La política d'errors: EAFP davant de LBYL
Tota aplicació necessita una política: què es captura i què es deixa pujar. La regla és fàcil de recordar: captura un error només si hi pots fer alguna cosa útil —demanar la dada un altre cop, fer servir un valor per defecte, avisar l'usuari—. Si no pots, deixa'l pujar: una fallada visible és infinitament millor que una de silenciosa. Per validar hi ha a més dos estils, i Python en prefereix un:
if ruta.exists(): # LBYL (Look Before You Leap), com a 05-05
with open(ruta, encoding="utf-8") as f:
dades = json.load(f)
else:
dades = []
try: # EAFP (Easier to Ask Forgiveness than Permission)
with open(ruta, encoding="utf-8") as f:
dades = json.load(f)
except FileNotFoundError: # l estil que prefereix Python
dades = []| Estil | Avantatge | Inconvenient |
|---|---|---|
| LBYL | Es llegeix com una condició normal | Deixa una escletxa: el fitxer es pot esborrar entre la comprovació i l'obertura |
| EAFP | No hi ha escletxa: o s'obre o es captura | Requereix conèixer l'excepció concreta |
Python prefereix EAFP, i amb fitxers és objectivament més segur: entre l'exists() i l'open() passa un instant en què un altre programa pot esborrar o moure el fitxer. Aquella comprovació amb Path.exists() de 05-05 era correcta per al que sabíem llavors; aquesta és la versió robusta que vam prometre.
logging davant de print
logging davant de printQuan alguna cosa falla, la temptació és sembrar el codi de print("he arribat aqui"). Funciona per sortir del pas, però en un programa real és un mal sistema: després s'han d'esborrar (i sempre se n'oblida algun), es barregen amb la sortida legítima i no deixen constància de res. El mòdul logging de la biblioteca estàndard resol les tres coses.
| Nivell | Quan fer-lo servir | Exemple a TascaFàcil |
|---|---|---|
DEBUG |
Detall intern útil en programar | «Carregades 12 tasques de tasques.json» |
INFO |
Esdeveniments normals | «Tasca creada: Cartell fira del llibre» |
WARNING |
Alguna cosa estranya, però es pot continuar | «El fitxer no existeix; agenda buida» |
ERROR / CRITICAL |
Una operació ha fallat / no es pot continuar | «No s'ha pogut desar el JSON» |
import logging
logging.basicConfig(
filename="tascafacil.log", # sense aixo, surt per pantalla
level=logging.INFO, # es registren INFO i superiors
format="%(asctime)s %(levelname)s %(message)s",
)
try:
desar(agenda)
except OSError:
logging.exception("Error en desar") # inclou el traceback completTres detalls marquen la diferència. level decideix què es registra: pujant-lo a WARNING silencies els missatges de detall sense tocar ni una línia de codi. filename envia tot a un fitxer, així que demà pots veure què va passar sense haver estat al davant. I logging.exception(), fet servir dins d'un except, escriu el missatge i el traceback sencer: és la manera correcta de registrar una fallada que has capturat i no vols perdre.
- Depurar de debò: mètode, VS Code i
pdb
pdbDepurar no és mirar el codi fixament fins que confessi. És un mètode de quatre passos:
- Reproduir: trobar la seqüència exacta que provoca la fallada, sempre. Un error que no saps reproduir no el pots arreglar.
- Aïllar: reduir el cas al mínim. Falla amb una tasca o en calen vint? Amb qualsevol prioritat?
- Formular una hipòtesi concreta («crec que
diesarriba com a text des del JSON») i comprovar-la: una sola cosa cada vegada; si és falsa, es descarta i se'n formula una altra.
Per a aquest últim pas hi ha el depurador. A VS Code fas clic a l'esquerra del número de línia per posar un punt d'interrupció (un cercle vermell), engegues amb F5 i el programa s'atura just abans d'executar aquella línia; allà pots inspeccionar les variables al panell lateral, veure la pila de crides i avançar amb F10 (executa la línia sencera), F11 (entra a la funció) i F5 (continua). Sense editor gràfic, escriu breakpoint() a la línia que t'interessi i executa: s'obrirà una consola (Pdb) en aquell punt.
| Comanda | Què fa |
|---|---|
l (list) |
Mostra el codi al voltant de la línia actual |
n (next) |
Executa la línia i passa a la següent |
s (step) |
Entra dins de la funció que es crida |
c (continue) |
Continua fins al breakpoint() següent |
p expressio |
Imprimeix el valor d'una variable o expressió |
w (where) / q (quit) |
Mostra la pila de crides / surt del depurador |
I una última eina, en dues línies: assert condicio, "missatge" llança un AssertionError si la condició és falsa. Serveix per comprovar suposicions internes mentre desenvolupes, però no per validar dades de l'usuari, perquè Python pot desactivar els assert amb l'opció -O. El seu lloc natural són les proves, i allà ho reprenem a 08-04.
- TascaFàcil v0.19: el programa que no es trenca
Primer, magatzem.py sobreviu que el JSON no existeixi o estigui corrupte:
"""Persistencia de TascaFacil: JSON i exportacio a CSV."""
RUTA_JSON = Path("tasques.json")
def carregar_tasques(ruta: Path = RUTA_JSON) -> Agenda:
"""Retorna l agenda desada; una agenda buida si no s ha pogut llegir."""
try:
with open(ruta, encoding="utf-8") as f:
return Agenda.from_dict(json.load(f))
except FileNotFoundError:
logging.info("No existeix %s; es comenca amb una agenda buida.", ruta)
except json.JSONDecodeError as error:
logging.error("El fitxer %s esta malmes: %s", ruta, error)
ruta.replace(ruta.with_suffix(".json.bak")) # no es perd: s aparta
return Agenda()Fixa't en la decisió de disseny: davant d'un fitxer corrupte no s'esborra res, s'aparta amb un altre nom i es continua, de manera que l'usuari no perd les seves dades. Segon, el model valida i llança la seva pròpia excepció, i la interfície la captura per tornar a preguntar:
class Tasca:
def __init__(self, titol: str, responsable: str, prioritat: str = "mitjana",
dies: int = 1) -> None:
if prioritat.strip().lower() not in PRIORITATS:
raise TascaInvalida(f"Prioritat no valida: {prioritat!r}")
try:
self.dies = int(dies)
except (ValueError, TypeError) as error:
raise TascaInvalida(f"Dies no valid: {dies!r}") from error
def demanar_prioritat() -> str: # a tascafacil/interficie.py
"""Demana una prioritat fins que sigui valida."""
while True:
try:
return Tasca("temporal", "marta", input("Prioritat: ")).prioritat
except TascaInvalida as error:
print(f" {error} Torna-ho a provar.")I a __main__.py, la configuració del registre i una xarxa de seguretat final perquè cap fallada inesperada no s'endugui per davant les dades:
def main() -> None:
"""Executa TascaFacil registrant els esdeveniments a tascafacil.log."""
logging.basicConfig(filename="tascafacil.log", level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s")
agenda = magatzem.carregar_tasques()
try:
bucle_principal(agenda)
except KeyboardInterrupt: # l usuari ha premut Ctrl+C
print("\nSortint...")
finally:
magatzem.desar(agenda) # passi el que passi, es desaTascaFàcil v0.19: un JSON corrupte ja no tomba el programa, una prioritat inventada es rebutja amb un missatge del domini, un dies de text es converteix o es rebutja amb claredat, un Ctrl+C desa abans de sortir i tot queda registrat a tascafacil.log.
Errors Habituals i Consells
- Fer servir
except:oexcept Exceptionper comoditat, o capturar i no fer res (except ValueError: pass). Amaguen errors de programació i creen fallades invisibles: captura l'excepció concreta, i si no hi pots fer res útil, deixa-la pujar. - Posar l'
exceptgeneral abans que els concrets, o ficar mig programa dins deltry. Es comproven en ordre, així que el primer que encaixi guanya; i altryhi ha d'anar només la línia que pot fallar, amb la resta a l'else. - Llegir el traceback per sobre, o depurar amb
printen un programa real. L'última línia diu què ha passat i l'última línia teva diu on; i per a la resta hi halogging, que es filtra per nivell, va a un fitxer i no s'ha d'esborrar després. - Consell: escriu el missatge pensant en qui l'ha de llegir. «Prioritat no valida: 'urgent'. Fes servir alta, mitjana o baixa» val mil vegades més que «error de validació».
Exercicis
Exercici 1: Llegir un traceback
Explica què ha passat, en quina línia de codi propi hi ha el problema i com ho arreglaries:
Traceback (most recent call last):
File "informes.py", line 20, in <module>
print(mitjana_dies(tasques))
File "informes.py", line 15, in mitjana_dies
return sum(t["dies"] for t in tasques) / len(tasques)
ZeroDivisionError: division by zeroExercici 2: De fràgil a robust
Reescriu aquesta funció perquè sobrevisqui a un fitxer inexistent, a una línia mal formada i a un valor no numèric, registrant cada problema amb logging i retornant el que sí que s'hagi pogut llegir:
def llegir_hores(ruta):
hores = []
with open(ruta, encoding="utf-8") as f:
for linia in f:
nom, valor = linia.split(",")
hores.append((nom, int(valor)))
return horesExercici 3: Excepció pròpia
Defineix una excepció PressupostInvalid per a l'Estudi Alba i una funció validar_pressupost(import_, client) que la llanci si l'import no és un número, si és negatiu o si supera els 50.000 € (límit que exigeix l'aprovació de la Marta). Escriu després el try/except que la fa servir mostrant un missatge diferent en cada cas.
Solucions
Solució 1.
L'última línia diu què: una divisió per zero. L'última línia de codi propi diu on: informes.py, line 15, a mitjana_dies. Com que sum(...) no divideix, el zero només pot ser len(tasques): la llista de tasques està buida. L'error real, però, no és a la línia 15 sinó a qui ha cridat des de la línia 20 amb una llista buida; la funció només l'ha manifestat.
def mitjana_dies(tasques: list[dict]) -> float:
"""Retorna la mitjana de dies estimats; 0.0 si no hi ha tasques."""
if not tasques: # LBYL: cas previst i legitim
return 0.0
return sum(t["dies"] for t in tasques) / len(tasques)Si una llista buida fos un cas impossible —un error de programació en qui crida—, el correcte seria el contrari: no capturar-lo i deixar que el ZeroDivisionError faci soroll.
Solució 2.
def llegir_hores(ruta: str) -> list[tuple[str, int]]:
"""Retorna les hores llegides del fitxer, saltant les linies invalides."""
hores: list[tuple[str, int]] = []
try:
linies = Path(ruta).read_text(encoding="utf-8").splitlines()
except FileNotFoundError:
logging.error("No existeix el fitxer d hores: %s", ruta)
return hores # llista buida: el programa continua
for numero, linia in enumerate(linies, start=1):
try:
nom, valor = linia.strip().split(",")
hores.append((nom.strip().lower(), int(valor)))
except ValueError as error: # cobreix el split i l int
logging.warning("Linia %d ignorada: %s", numero, error)
return horesDues idees importants. El try interior embolcalla només les dues línies que poden fallar, i captura ValueError perquè tant un split que no retorna dues parts com un int("vuit") llancen aquesta mateixa excepció. I la fallada d'una línia no avorta el fitxer sencer: es registra amb el seu número i el bucle continua. Aquesta és la diferència entre un programa fràgil i un de robust: continua fent tot el que sí que pot fer.
Solució 3.
LIMIT_APROVACIO = 50000 # a partir d aqui, ho aprova la Marta
class PressupostInvalid(ValueError):
"""L import d un pressupost no compleix les regles de l Estudi Alba."""
def validar_pressupost(import_, client: str) -> float:
"""Retorna l import validat o llanca PressupostInvalid."""
try:
import_ = float(import_)
except (TypeError, ValueError) as error:
raise PressupostInvalid(f"Import no numeric: {import_!r}") from error
if import_ < 0:
raise PressupostInvalid(f"Import negatiu per a {client}: {import_}")
if import_ > LIMIT_APROVACIO:
raise PressupostInvalid(f"{import_} EUR supera el limit; ho aprova la Marta.")
return import_
try:
total = validar_pressupost("12500", "Forn Sole")
except PressupostInvalid as error:
print(f"Pressupost rebutjat: {error}")
else: # nomes si no hi ha hagut excepcio
print(f"Pressupost acceptat: {total:.2f} EUR")Fixa't en el from error de la primera conversió: conserva la causa original al traceback, de manera que qui depuri sabrà que darrere de PressupostInvalid hi havia un ValueError de float(). Els tres missatges són diferents i diuen què cal fer, que és la marca d'un bon error. I l'else deixa clar que la línia d'èxit només s'executa si no hi ha hagut excepció.
Conclusió
Els errors són de tres tipus: els de sintaxi impedeixen executar, els d'execució llancen excepcions i els de lògica no avisen de res i només es cacen depurant i provant. Davant d'una excepció, el traceback ho explica tot: l'última línia diu què ha passat, l'última línia de codi propi diu on, i l'origen real sol ser més amunt a la pila. Reconèixer el catàleg habitual —ValueError, TypeError, KeyError, IndexError, FileNotFoundError, ZeroDivisionError, AttributeError, json.JSONDecodeError— estalvia la meitat de la feina. Amb try/except es captura l'excepció concreta que saps gestionar, mai except: a seques ni except Exception, col·locant els casos concrets abans que els generals, agrupant-ne diversos en una tupla i recollint l'objecte amb as error; else executa el que només té sentit si no hi ha hagut fallada i finally garanteix la neteja passi el que passi, fins i tot quan l'excepció continua pujant. Amb raise llances errors expressament per fallar aviat i amb un missatge clar, raise a seques rellança sense perdre el traceback i raise ... from conserva la causa. Una excepció pròpia com TascaInvalida, heretada de ValueError, aporta precisió en capturar i missatges del domini. La política es resumeix en una frase: captura només el que puguis resoldre i deixa pujar la resta, preferint EAFP a LBYL quan hi ha fitxers pel mig. Per saber què està passant, logging substitueix el print amb els seus cinc nivells, la seva sortida a fitxer i el seu logging.exception(). I per als errors de lògica, el mètode —reproduir, aïllar, formular hipòtesis, comprovar— juntament amb els punts d'interrupció de VS Code i breakpoint()/pdb.
TascaFàcil és ara la v0.19: un tasques.json corrupte s'aparta amb una còpia en lloc de tombar el programa, una prioritat inventada es rebutja amb TascaInvalida i la interfície torna a preguntar, un dies que arriba com a text es converteix o s'explica, un Ctrl+C desa abans de sortir i tot queda anotat a tascafacil.log. El programa aguanta. Però fixa't en el que acabes de fer: has canviat magatzem.py, model.py, interficie.py i __main__.py alhora, i no hi ha manera de tornar enrere si alguna cosa d'això n'ha empitjorat una altra. No existeix cap còpia de l'estat anterior, ni cap registre de què es va tocar ni per què. Això s'acaba a Control de versions: Git, l'historial del projecte, i la tranquil·litat de poder experimentar sabent que no es perd res.
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ó
