Comprensione di dict.copy () - superficiale o profonda?


429

Durante la lettura della documentazione per dict.copy(), dice che fa una copia superficiale del dizionario. Lo stesso vale per il libro che sto seguendo (Beazley's Python Reference), che dice:

Il metodo m.copy () crea una copia superficiale degli elementi contenuti in un oggetto di mappatura e li inserisce in un nuovo oggetto di mappatura.

Considera questo:

>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}

Quindi ho pensato che questo avrebbe aggiornato il valore di original(e aggiunto 'c': 3) anche da quando stavo facendo una copia superficiale. Come se lo facessi per un elenco:

>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])

Funziona come previsto.

Dal momento che entrambi sono copie superficiali, perché dict.copy()non funziona come mi aspetto? O la mia comprensione della copia superficiale vs profonda è errata?


2
Caratteristico che non spiegano "superficiale". Conoscenza privilegiata, occhiolino. Solo il dict e le chiavi sono una copia mentre i dadi nidificati all'interno di quel primo livello sono riferimenti, ad esempio non possono essere eliminati in un ciclo. Quindi il dict.copy () di Python in quel caso non è né utile né intuitivo. Grazie per la tua domanda
gseattle,

Risposte:


991

Con "copia superficiale" significa che il contenuto del dizionario non viene copiato per valore, ma semplicemente creando un nuovo riferimento.

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

Al contrario, una copia profonda copierà tutti i contenuti in base al valore.

>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

Così:

  1. b = a: Assegnazione di riferimento, marca ae bpunta allo stesso oggetto.

    Illustrazione di "a = b": "a" e "b" indicano entrambi "{1: L}", "L" indica "[1, 2, 3]".

  2. b = a.copy(): Copia superficiale ae bdiventerà due oggetti isolati, ma i loro contenuti condividono ancora lo stesso riferimento

    Illustrazione di 'b = a.copy ()': 'a' indica '{1: L}', 'b' indica '{1: M}', 'L' e 'M' indicano entrambi '[ 1, 2, 3] '.

  3. b = copy.deepcopy(a): Copia profonda, ae la bstruttura e il contenuto diventare 's completamente isolati.

    Illustrazione di 'b = copy.deepcopy (a)': 'a' indica '{1: L}', 'L' indica '[1, 2, 3]';  'b' indica '{1: M}', 'M' indica un'istanza diversa di '[1, 2, 3]'.


Bella risposta, ma potresti considerare di correggere l'errore grammaticale nella tua prima frase. E non c'è motivo di non Lriutilizzarlo b. Ciò semplificherebbe l'esempio.
Tom Russell,

@kennytm: Qual è la differenza tra i primi due esempi, in effetti? Si ottiene lo stesso risultato, ma un'implementazione interna leggermente diversa, ma per cosa importa?
JavaSa,

@TomRussell: O chiunque, dato che questa domanda è piuttosto vecchia, la mia domanda di chiarimento è per tutti
JavaSa

@JavaSa Importa se, diciamo, lo fai b[1][0] = 5. Se bè una copia superficiale, hai appena cambiato a[1][0].
Tom Russell,

2
Grande spiegazione, ... mi ha davvero salvato la giornata! Grazie ... Lo stesso può essere applicato all'elenco, str e altri tipi di dati di Python?
Bhuro,

38

Non è una questione di copia profonda o copia superficiale, niente di ciò che stai facendo è copia profonda.

Qui:

>>> new = original 

stai creando un nuovo riferimento all'elenco / dict a cui fa riferimento l'originale.

mentre qui:

>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)

stai creando un nuovo elenco / dict che viene riempito con una copia dei riferimenti degli oggetti contenuti nel contenitore originale.


31

Prendi questo esempio:

original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()

Ora cambiamo un valore nel livello 'superficiale' (primo):

new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer

Ora cambiamo un valore a un livello più profondo:

new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed

8
no change in original, since ['a'] is an immutable integerQuesto. In realtà risponde alla domanda posta.
CivFan,

8

Aggiungendo alla risposta di kennytm. Quando si esegue una copia superficiale parent.copy () viene creato un nuovo dizionario con le stesse chiavi, ma i valori non vengono copiati a cui viene fatto riferimento. Se si aggiunge un nuovo valore a parent_copy, non avrà effetto su parent poiché parent_copy è un nuovo dizionario non riferimento.

parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent

print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400

print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128

parent_copy[1].append(4)
parent_copy[2] = ['new']

print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}

Il valore hash (id) di parent [1] , parent_copy [1] sono identici, il che implica [1,2,3] di parent [1] e parent_copy [1] memorizzati nell'id 140690938288400.

Ma l'hash di parent e parent_copy sono diversi, il che implica che sono dizionari diversi e parent_copy è un nuovo dizionario con valori che fanno riferimento a valori di parent


5

"new" e "original" sono dicts diversi, ecco perché puoi aggiornarne solo uno. Gli oggetti sono poco copiati, non il dict stesso.


2

I contenuti vengono copiati in modo superficiale.

Quindi, se l'originale dictcontiene uno listo l'altro dictionary, modificandone uno nell'originale o la sua copia superficiale li modificherà (l'uno listo l'altro dict) nell'altro.


1

Nella tua seconda parte, dovresti usare new = original.copy()

.copye =sono cose diverse.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.