HOWTO · Python
Copia un dizionario in Python
Scopri quando l’assegnazione crea un alias di un dizionario, quando basta una copia superficiale e quando serve una copia profonda per rendere indipendenti i valori annidati.
In questa pagina
Per creare un nuovo dizionario esterno, usa dict(source) oppure source.copy(). Entrambi producono copie superficiali: i valori annidati restano condivisi. Usa copy.deepcopy(source) quando anche i valori mutabili annidati devono essere indipendenti. L’assegnazione con = non crea una copia. Per approfondire, consulta il tutorial sui dizionari Python e la documentazione del modulo copy di Python.
Assegnare un dizionario crea un alias
L’assegnazione associa un altro nome allo stesso oggetto dizionario. Le modifiche apportate al dizionario tramite uno dei due nomi sono visibili anche tramite l’altro. Riassegnare un nome è diverso: quel nome punta a un altro oggetto, senza modificare l’oggetto originale né l’altro nome.
Questo esempio completo modifica il dizionario condiviso tramite meal, poi assegna a meal un nuovo dizionario:
food = {"Fruit": "Orange", "Vegetable": "Lettuce"}
meal = food
meal["Fruit"] = "Banana"
print(food)
print(meal)
meal = {"Fruit": "Apple"}
print(food)
print(meal)
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Apple'}
I primi due valori stampati coincidono perché, in quel momento, entrambi i nomi si riferiscono allo stesso oggetto. Dopo l’assegnazione a meal, solo quel nome punta al nuovo dizionario; food continua a riferirsi all’originale. Questa distinzione spiega perché modificare un valore tramite un nome si riflette anche nell’altro, mentre assegnare un nuovo oggetto a una variabile non sostituisce quello a cui si riferisce l’altra.
Creare una copia superficiale di un dizionario
Il costruttore incorporato dict() e il metodo copy() di un dizionario creano ciascuno un nuovo dizionario esterno. Aggiornare o rimuovere una voce di primo livello nella copia non modifica l’originale. I due dizionari continuano però a riferirsi alle stesse liste, agli stessi dizionari annidati o ad altri valori mutabili; quindi le modifiche a questi valori possono essere visibili tramite entrambi i nomi.
Qui by_constructor e by_method ricevono ciascuno un proprio dizionario esterno. La lista annidata è condivisa, mentre assegnare un nuovo valore alla chiave name modifica solo la copia selezionata:
source = {"name": "Sherlock", "numbers": [1, 2, 3]}
by_constructor = dict(source)
by_method = source.copy()
by_constructor["name"] = "Holmes"
by_constructor["numbers"][1] = 4
print(source)
print(by_constructor)
print(by_method)
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
{'name': 'Holmes', 'numbers': [1, 4, 3]}
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
Scegli una delle due forme di copia superficiale quando vuoi rendere indipendente il dizionario esterno e ti va bene condividere i valori annidati. Una copia superficiale non copia ricorsivamente gli oggetti annidati. Per esempio, assegnare un valore diverso a by_constructor["name"] lascia invariato name nella sorgente, perché la chiave appartiene a ciascun dizionario esterno separato. Invece, modificare un elemento di numbers si riflette in tutti e tre i dizionari stampati, perché la lista è condivisa. Lo stesso vale quando un valore è un altro dizionario: la copia di primo livello conserva un riferimento a quel dizionario interno. Se devi rendere indipendente un solo valore annidato, puoi copiare quel valore separatamente; usa una copia profonda quando vuoi rendere indipendente l’intera struttura annidata.
Copiare in profondità un dizionario annidato
Quando anche i valori mutabili annidati devono avere copie indipendenti, usa copy.deepcopy(). La copia profonda copia ricorsivamente gli oggetti contenuti, perciò l’esempio seguente può modificare un elemento della lista e un valore del dizionario interno senza cambiare l’originale. È utile per i dati annidati che vuoi modificare in modo indipendente; non serve quando è previsto condividere i valori annidati.
import copy
source = {
"numbers": [1, 2, 3],
"address": {"city": "Miami"},
}
independent = copy.deepcopy(source)
independent["numbers"][1] = 4
independent["address"]["city"] = "Lexington"
print(source)
print(independent)
{'numbers': [1, 2, 3], 'address': {'city': 'Miami'}}
{'numbers': [1, 4, 3], 'address': {'city': 'Lexington'}}
Una copia profonda è utile quando le modifiche successive non devono propagarsi tra una struttura sorgente e una copia di lavoro, per esempio quando prepari una versione di impostazioni annidate da modificare separatamente. Copia anche i contenitori annidati, non solo il dizionario esterno: per questo, nell’esempio, restano invariati sia la lista sia il dizionario address interno. Per strutture grandi, la copia profonda può richiedere più tempo e memoria di una copia superficiale; scegli quindi in base alla necessità di rendere indipendenti gli oggetti annidati. Gli oggetti Python possono personalizzare il comportamento di copia e alcune risorse non possono essere duplicate in modo significativo; se lavori con oggetti di questo tipo, consulta la documentazione del modulo copy.
La scelta pratica dipende da quali oggetti devono essere condivisi: l’assegnazione condivide l’intero dizionario, una copia superficiale separa solo il dizionario esterno e una copia profonda separa anche gli oggetti annidati. La documentazione del modulo copy descrive come le copie superficiali e profonde trattano gli oggetti composti e quelli con un comportamento di copia personalizzato.