Python – Perché la mia funzione mantiene i valori tra le chiamate?

👤 Iniziato da @matteo77
📅 23/05/2025 23:09
📁 Programmazione 🌐 IT
Avatar di matteo77
Ciao a tutti! Ho scritto questa funzione per aggiungere un elemento a una lista:

def aggiungi_elemento(elem, lista=[]):
lista.append(elem)
return lista

print(aggiungi_elemento(1))
print(aggiungi_elemento(2))
print(aggiungi_elemento(3))

Ma invece di restituire liste separate, mi accumula i valori ogni volta!
Cosa sto sbagliando?
Avatar di moiragatti
Ciao Matteo, ho dato un'occhiata al tuo codice e capisco perfettamente il casino che sta succedendo – è una di quelle trappole classiche di Python che mi hanno fatto imprecare più di una volta, soprattutto quando ero di fretta al mattino!

Il problema è che i parametri predefiniti come le liste vengono creati una volta sola, quando la funzione viene definita, e poi riutilizzati in tutte le chiamate successive. Quindi, ogni volta che chiami `aggiungi_elemento()` senza passare una lista esplicita, stai lavorando sulla stessa identica lista che si accumula valori. È come se avessi una lista globale nascosta, e non è quello che volevi, vero?

Per sistemarlo, usa `None` come valore predefinito e poi controlla e crea la lista all'interno della funzione. Prova così:

```python
def aggiungi_elemento(elem, lista=None):
if lista is None:
lista = [] # Crea una nuova lista ogni volta
lista.append(elem)
return lista

print(aggiungi_elemento(1)) # [1]
print(aggiungi_elemento(2)) # [2]
print(aggiungi_elemento(3)) # [3]
```

Ora dovrebbe funzionare come ti aspetti, con liste separate ogni volta. È una buona pratica per evitare questi imbrogli con oggetti mutabili come le liste o i dizionari.

Se stai imparando Python, continua così – queste cose capitano, e una volta capite ti fanno apprezzare quanto è potente il linguaggio. Se hai altre domande su questo o altro, dimmi! 😊
Avatar di lennonbarbieri7
Ah, la classica trappola dei parametri mutabili in Python! @moiragatti ha già dato una spiegazione perfetta, ma ci tengo a sottolineare una cosa: questa cosa ha mandato in crisi me pure la prima volta. Ricordo che passai un pomeriggio intero a bestemmiare prima di capire che il problema era quel maledetto `[]` come default.

Se vuoi un consiglio spassionato, **evita come la peste** di usare liste, dizionari o oggetti mutabili come parametri di default. Quella sintassi sembra innocente, ma è un campo minato. Il trucco con `None` è oro colato, usalo sempre.

E se ti senti confuso, sappi che anche Guido van Rossum (il creatore di Python) ha ammesso che forse avrebbero dovuto progettare questa cosa diversamente. Quindi non è colpa tua, è proprio un comportamento controintuitivo del linguaggio.

P.S. Se vuoi approfondire, cerca "mutable default arguments Python" e troverai montagne di post di gente che ha sbattuto la testa come noi. Benvenuto nel club! 🎸
Avatar di novabarbieri83
Anch'io sono cascata nella stessa trappola tempo fa. La cosa che mi ha aiutato a capire meglio il problema è stato leggere la documentazione ufficiale di Python su come vengono gestiti i valori di default per gli argomenti delle funzioni. In pratica, quando definisci una funzione, gli argomenti di default vengono valutati una sola volta e quel valore viene riutilizzato ogni volta che la funzione viene chiamata senza specificare quell'argomento. Quindi, se usi una lista come valore di default, quella lista viene creata una volta e poi riutilizzata, accumulando i valori.

La soluzione proposta da @moiragatti è ottimale: usare `None` come valore di default e creare la lista dentro la funzione. È una buona pratica non solo per evitare questo tipo di problema, ma anche per rendere il codice più prevedibile e facile da capire.

Se vuoi approfondire, c'è un articolo interessante su Real Python che spiega bene questo comportamento e come gestirlo al meglio. Comunque, concordo con @lennonbarbieri7: evita i parametri mutabili come la peste!
Avatar di beniaminosala21
Ecco, questa è una di quelle cose che ti fanno venire voglia di lanciare il monitor dalla finestra la prima volta che le incontri. @matteo77, tranquillo, non sei il primo e non sarai l'ultimo a cadere in questa trappola.

La spiegazione di @moiragatti è spot on: Python valuta i default **una volta sola** al momento della definizione della funzione, non a ogni chiamata. Quindi quel `[]` diventa una sorta di "lista fantasma" che persiste tra le chiamate, e ogni append ci sguazza dentro come se fosse una piscina comune.

Il fix con `None` è la via maestra, ma aggiungo un dettaglio che pochi considerano: se per qualche motivo insensato volessi *davvero* che il comportamento fosse quello originale (mantenere lo stato tra chiamate), allora dovresti documentarlo **molto bene**, perché è un side effect che può fare danni enormi in codice complesso. Ma 99,9% dei casi, no.

Piccolo consiglio extra: se usi PyCharm o VS Code, alcuni linter ti avvisano subito di questo antipattern con un warning tipo *"Default argument value is mutable"*. Attivalo e risparmierai bestemmie future.

E per chi dice "ma perché Python fa così?", la risposta è: legacy + ottimizzazione. Non è un bel design, ma ormai è scolpito nella pietra. Viviamo e impariamo.

(Comunque, se ti capita di nuovo, pensa a noi che abbiamo bruciato neuroni sullo stesso problema e sorridi. È un rito di passaggio.)
Avatar di salemrizzo34
Ah, che incubo, @matteo77! È una di quelle trappole classiche di Python che ti fa perdere ore preziose, e devo dire che @lennonbarbieri7 ha centrato il punto con la storia del "campo minato". Anch'io ci sono cascato anni fa su un progetto banale, e la frustrazione è stata reale – roba che ti viene voglia di spegnere il PC e andare a fare una passeggiata per schiarirti le idee.

Però, a parte il fix con None che tutti stanno consigliando (e che è davvero la scelta più furba), io ti suggerisco di andare oltre e controllare se stai usando qualche framework o libreria che potrebbe amplificare questo problema. Ad esempio, se il tuo codice è parte di un'app più grande, tipo con Flask o Django, assicurati che non ci siano altri shared state che si accumulano da altre parti. È facile che una cosa del genere si propaghi e ti morde inaspettatamente.

E su una nota più pratica, @beniaminosala21 ha menzionato i linter, e concordo al 100%: PyLint o Flake8 sono ottimi per beccare questi errori sul nascere. Io li ho impostati come abitudine in VS Code, e mi hanno salvato da un sacco di mal di testa. Se non li usi ancora, dai un'occhiata, perché eviterai di ripetere l'errore in futuro.

Comunque, non fasciarti la testa: una volta capito, diventi più scaltro. Se hai il codice completo del tuo script, potresti postarlo qui per un check rapido – magari c'è qualcos'altro che sfugge. Forza, rimettiti al lavoro! 😤
Avatar di raquel.pérez
Oh mamma, leggere queste discussioni mi fa sempre sorridere! Questa trappola del default mutable è tipo il "cane che si morde la coda" di Python: sembra innocuo finché non ti ritrovi con una lista che ad ogni chiamata diventa più lunga del mio elenco di cose da fare (e fidati, non è uno scherzo 😂).

@matteo77, la soluzione è davvero quella di usare `None` come valore di default, così:

```python
def aggiungi_elemento(elem, lista=None):
if lista is None:
lista = []
lista.append(elem)
return lista
```

Così ogni volta che chiami la funzione senza specificare la lista, ne crei una nuova fresca di giornata, mica la piscina condivisa!

E, aggiungo, questa cosa è un buon promemoria per chiunque inizi a programmare: mai sottovalutare i valori di default mutabili. Sono come quei regali che sembrano carini ma poi ti ritrovi con la sorpresa amara dentro. Se non ci credi, prova a farlo sul serio e vedrai che la lista si trasforma in un party continuo di numeri accumulati!

Per chi usa PyCharm o VS Code, attivate i linter e non solo vi eviterete queste sorprese ma vi sentirete pure un po’ dei ninja del codice—e chi non vuole sentirsi un ninja, eh?

Ultima chicca: per non morire di noia con queste cose, consiglio di alternare il debugging a qualche pausa con una buona serie o, se possibile, una passeggiata. Fidati, torna tutto più chiaro e il cervello fa meno "buffer overflow"!

Insomma, non è un bug, è una feature... ma una feature subdola che va domata! 💪🔥
Avatar di domenicaconti
@raquel.pérez, sono totalmente d'accordo con te! La storia dei default mutabili in Python è una di quelle cose che ti fa venire voglia di mettere su una playlist di emergenza per sfogarti un po'! La mia playlist è un viaggio schizofrenico tra generi e decenni diversi, e ogni volta che incontro un problema del genere, cambio canzone e mi rituffo nel codice.
Avatar di angel64Re
@domenicaconti, il fatto che servano playlist di emergenza la dice lunga sulla frustrazione che questa trappola provoca, e non è poco. Però, detto con chiarezza, affidarsi solo a distrazioni musicali non risolve il problema alla radice. L’unica cura seria è capire come Python gestisce i valori di default, e soprattutto fare pratica con pattern più sicuri. Usare `None` come default è la base, ma non basta fermarsi lì: se il codice cresce, quei dettagli diventano mine nascoste che ti fanno perdere tempo e credibilità.

Se vuoi un consiglio utile, ti suggerisco di leggere *Fluent Python* di Luciano Ramalho: spiega bene queste dinamiche “subdole” e ti dà strumenti per scrivere codice più robusto senza impazzire ogni volta che qualcosa si accumula senza motivo. Quanto alla playlist, va bene per il momento di sfogo, ma la vera soddisfazione arriva quando smetti di doverla usare per questi errori evitabili. Altrimenti si rischia di restare in un loop infinito di frustrazione e “cambi canzone”.
Avatar di scottS27
@angel64Re condivido al 100% la tua analisi, soprattutto sul fatto che affidarsi solo a espedienti come playlist di sfogo sia un palliativo che non risolve il vero problema. È frustrante vedere anche sviluppatori con un po’ di esperienza inciampare in queste trappole di Python, proprio perché sono così insidiose e “silenziose”. Usare `None` come valore di default è un passo obbligato, ma la vera differenza la fa capire i meccanismi più profondi del linguaggio e adottare pattern di progettazione più robusti.

Sul libro di Ramalho non posso che confermare: *Fluent Python* è una bibbia per chi vuole padroneggiare davvero certi aspetti subdoli e scrivere codice che dura nel tempo. Personalmente, credo che investire tempo nella lettura di testi del genere paghi sempre più di mille trucchi rapidi e soluzioni estemporanee.

Infine, un consiglio a chi si arrabbia con questi errori: la frustrazione è normale, ma trasformarla in voglia di capire a fondo è ciò che fa la differenza tra un programmatore che si lamenta e uno che migliora davvero. Meglio un loop di apprendimento che uno di cambi canzone.

La Tua Risposta

💬

Vuoi partecipare alla discussione?

Accedi o registrati per scrivere la tua risposta e unirti alla conversazione!