
Il debugging è una delle attività più importanti nello sviluppo software. Scrivere codice corretto al primo colpo è un'illusione: il vero programmatore non è quello che non sbaglia mai, ma quello che sa come individuare e correggere rapidamente i problemi. In Python, grazie alla sua sintassi chiara e alla grande disponibilità di strumenti, il debugging può diventare un processo strutturato, rapido ed efficace. Ma serve un approccio ragionato, una certa disciplina, e la padronanza degli strumenti giusti.
Capire il problema: il traceback non mente
Il punto di partenza è sempre lo stesso: osservare. Quando Python genera un errore, fornisce uno stack trace molto dettagliato. Non ignorarlo. Ecco un esempio di errore classico:
def divide(a, b):
return a / b
print(divide(10, 0))
Output:
Traceback (most recent call last):
File "example.py", line 4, in <module>
print(divide(10, 0))
File "example.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
In questo caso, il messaggio è chiaro. Ma in situazioni complesse con più funzioni annidate, leggere tutto lo stack trace, riga per riga, è l’unico modo per capire da dove parte il problema.
Il metodo print(): semplicità che funziona
Il debugging a colpi di print()
è grezzo, ma potente. Ti consente di vedere lo stato del tuo programma in tempo reale.
def calculate_total(items):
total = 0
for item in items:
print(f"Processing item: {item}") # debug
total += item["price"] * item["quantity"]
return total
cart = [
{"price": 10, "quantity": 2},
{"price": 5, "quantity": 3},
{"price": None, "quantity": 1}, # Bug qui
]
print(calculate_total(cart))
Qui ci accorgiamo che None
è il problema. Questo metodo è utile per validare input, controllare loop, capire il flusso. Ma va gestito: stampa troppo e ti perdi.
pdb: fermare il tempo nel codice
Quando il print()
non basta, entriamo nel mondo di pdb
, il debugger testuale di Python. Serve a mettere in pausa l'esecuzione, ispezionare variabili, avanzare riga per riga.
import pdb
def faulty_function(data):
total = 0
for d in data:
pdb.set_trace()
total += d
return total
numbers = [10, 20, "a", 40] # 'a' genera il problema
print(faulty_function(numbers))
Al momento di set_trace()
, il programma si ferma. Puoi usare comandi come:
-
n
per andare alla prossima riga -
p variable
per stampare una variabile -
c
per continuare l’esecuzione -
q
per uscire
Debugging da IDE: il potere della GUI
Chi usa PyCharm, VS Code o simili ha a disposizione strumenti visuali potenti. Metti un breakpoint con un clic, esegui passo-passo con F8, vedi tutte le variabili in tempo reale. Ecco un esempio di scenario ideale per l’IDE:
def apply_discount(price, discount):
if discount > 1:
raise ValueError("Discount must be between 0 and 1")
return price * (1 - discount)
print(apply_discount(100, 1.5)) # Scatena eccezione
Con un breakpoint sulla riga del raise
, puoi fermarti prima che l’errore venga lanciato, vedere i valori di price
e discount
e correggere la logica al volo.
Profiling: trova chi rallenta il codice
Non sempre il problema è un bug logico. A volte è la lentezza. Per questo esiste il profiling: misurare dove il codice spende più tempo.
import cProfile
def slow_function():
total = 0
for i in range(1000000):
total += i
return total
cProfile.run("slow_function()")
Output:
4 function calls in 0.059 seconds
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.057 0.057 0.059 0.059 example.py:3(slow_function)
...
Oppure, con line_profiler
, puoi misurare riga per riga. Aggiungi un decorator e lanci:
@profile
def calculate():
result = 0
for i in range(1000000):
result += i
return result
Le best practice che fanno la differenza
Il debugging non è improvvisazione. È metodo. Queste abitudini ti salvano la vita:
Isola il problema. Se hai una funzione che fallisce, portala fuori dal contesto. Costruisci un test minimale che lo riproduce.
Procedi per gradi. Cambia una riga, non dieci. Ogni modifica va testata subito.
Usa Git come diario. Ogni bug fix è una commit. Se peggiori la situazione, fai un passo indietro.
Evita l'effetto panico. Non riscrivere tutto. Se non capisci, fermati. Guarda il codice come se non fosse tuo.
Conclusione
Il debugging in Python è un’arte che si affina con il tempo. Non basta sapere che esiste pdb
: devi usarlo sul campo. Non basta cliccare su "Run": devi fermarti, leggere, osservare. Saper usare print()
, i debugger degli IDE, gli strumenti di profiling e una mentalità analitica è ciò che trasforma un programmatore normale in uno che risolve i problemi. Sempre.