Storia della questione: In Python, la copia di oggetti (soprattutto collezioni come liste e dizionari) è critica: una semplice assegnazione a una variabile crea un nuovo riferimento, non una copia. I metodi speciali copy() e deepcopy() sono stati introdotti per evitare effetti collaterali indesiderati nell'uso condiviso delle strutture.
Problema: Quando si lavora con collezioni annidate (lista di liste, dizionario dentro un dizionario), una semplice copy() riproduce solo il contenitore stesso, ma non gli elementi interni. Questo può portare a bug difficili da individuare: la modifica di un elemento annidato si riflette in tutte le "copie".
Soluzione: copy.copy() crea una copia superficiale (shallow copy) — un nuovo contenitore di livello superiore, ma gli oggetti annidati rimangono gli stessi. copy.deepcopy() copia ricorsivamente tutti gli oggetti annidati.
Esempio di codice:
import copy lst = [[1, 2], [3, 4]] shallow = copy.copy(lst) deep = copy.deepcopy(lst) lst[0][0] = 10 print(shallow) # [[10, 2], [3, 4]] — l'oggetto annidato è cambiato! print(deep) # [[1, 2], [3, 4]] — deepcopy ha mantenuto lo stato originale
Caratteristiche chiave:
È sufficiente a volte scrivere lst2 = lst[:] per copiare una lista?
lst2 = lst[:] crea una copia superficiale della lista, ma gli oggetti annidati (ad esempio, liste all'interno della lista) saranno comunque gli stessi. Per liste piatte, è sufficiente; per strutture annidate, non lo è.
Esempio:
lst = [[1], [2]] lst2 = lst[:] lst[0][0] = 99 print(lst2) # [[99], [2]] — l'elemento annidato è cambiato in entrambe le "copie"
Il metodo .copy() funziona allo stesso modo per tutte le collezioni?
No. Ad esempio, dict.copy() funziona come copia superficiale, list.copy() è apparso solo con Python 3.3, per set c'è set.copy(). Per oggetti utente, il supporto di .copy() dipende dal metodo implementato.
Si può farne a meno di deepcopy ovunque? È sicuro ed efficiente?
No. deepcopy è un'operazione costosa: può causare problemi di prestazioni, specialmente su strutture grandi, e può rompersi su oggetti non copiabili/chiusi o non standard. Usa deepcopy solo quando è davvero necessario un copia ricorsiva completamente indipendente.
Nei test si è tentato di copiare un "dizionario di dizionari" tramite dict.copy(). A causa di ciò, le modifiche nella struttura annidata per un utente influenzavano improvvisamente altri test (i dati venivano mutati globalmente).
Pro:
Contro:
È stato implementato copy.deepcopy(), descritto nella documentazione il livello di annidamento e le caratteristiche di ciascuna struttura d'oggetto, evitando deepcopy per grandi volumi, dove era possibile fare una copia "manuale" in parti.
Pro:
Contro: