create - python{}




Come copiare un dizionario e modificare solo la copia (12)

Anche questo mi ha confuso, inizialmente, perché venivo da uno sfondo C.

In C, una variabile è una posizione in memoria con un tipo definito. Assegnare a una variabile copia i dati nella posizione di memoria della variabile.

Ma in Python, le variabili agiscono più come puntatori agli oggetti. Assegnare una variabile a un'altra non crea una copia, ma fa in modo che quel nome di variabile punti allo stesso oggetto.

Qualcuno può spiegarmelo per favore? Questo non ha alcun senso per me.

Copio un dizionario in un altro e modifica il secondo ed entrambi sono cambiati. Perché sta succedendo?

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}

Come altri hanno spiegato, il dict incorporato non fa quello che vuoi. Ma in Python2 (e probabilmente anche in 3) puoi facilmente creare una classe ValueDict che copi con = così puoi essere sicuro che l'originale non cambierà.

class ValueDict(dict):

    def __ilshift__(self, args):
        result = ValueDict(self)
        if isinstance(args, dict):
            dict.update(result, args)
        else:
            dict.__setitem__(result, *args)
        return result # Pythonic LVALUE modification

    def __irshift__(self, args):
        result = ValueDict(self)
        dict.__delitem__(result, args)
        return result # Pythonic LVALUE modification

    def __setitem__(self, k, v):
        raise AttributeError, \
            "Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k)

    def __delitem__(self, k):
        raise AttributeError, \
            "Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k)

    def update(self, d2):
        raise AttributeError, \
            "Use \"value_dict<<=dict2\" instead of \"value_dict.update(dict2)\""


# test
d = ValueDict()

d <<='apples', 5
d <<='pears', 8
print "d =", d

e = d
e <<='bananas', 1
print "e =", e
print "d =", d

d >>='pears'
print "d =", d
d <<={'blueberries': 2, 'watermelons': 315}
print "d =", d
print "e =", e
print "e['bananas'] =", e['bananas']


# result
d = {'apples': 5, 'pears': 8}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
d = {'apples': 5, 'pears': 8}
d = {'apples': 5}
d = {'watermelons': 315, 'blueberries': 2, 'apples': 5}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
e['bananas'] = 1

# e[0]=3
# would give:
# AttributeError: Use "value_dict<<='0', ..." instead of "d[0] = ..."

Si prega di fare riferimento al modello di modifica lvalue discusso qui: Python 2.7: sintassi pulita per la modifica di lvalue . L'osservazione chiave è che str e int comportano come valori in Python (anche se in realtà sono oggetti immutabili sotto il cofano). Mentre lo stai osservando, per favore osserva anche che nulla è magicamente speciale su str o int . dict può essere usato più o meno allo stesso modo, e posso pensare a molti casi in cui ValueDict ha senso.


Le istruzioni di assegnazione in Python non copiano oggetti, creano collegamenti tra una destinazione e un oggetto.

così, dict2 = dict1 , risulta un altro legame tra dict2 e l'oggetto a cui dict1 riferisce dict1 .

se vuoi copiare un dict, puoi usare il copy module . Il modulo di copia ha due interfacce:

copy.copy(x)
Return a shallow copy of x.

copy.deepcopy(x)
Return a deep copy of x.

La differenza tra la copia superficiale e quella profonda è rilevante solo per gli oggetti composti (oggetti che contengono altri oggetti, come elenchi o istanze di classi):

Una copia superficiale costruisce un nuovo oggetto composto e quindi (per quanto possibile) inserisce riferimenti in esso agli oggetti trovati nell'originale.

Una copia profonda costruisce un nuovo oggetto composto e quindi, in modo ricorsivo, inserisce copie degli oggetti trovati nell'originale.

Ad esempio, in python 2.7.9:

>>> import copy
>>> a = [1,2,3,4,['a', 'b']]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(5)
>>> a[4].append('c')

e il risultato è:

>>> a
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> b
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> c
[1, 2, 3, 4, ['a', 'b', 'c']]
>>> d
[1, 2, 3, 4, ['a', 'b']]

Ogni variabile in python (cose come dict1 o str o __builtins__ è un puntatore a qualche "oggetto" platonico nascosto all'interno della macchina.

Se imposti dict1 = dict2 , basta puntare dict1 sullo stesso oggetto (o posizione di memoria, o qualsiasi altra analogia tu voglia) come dict2 . Ora, l'oggetto referenziato da dict1 è lo stesso oggetto referenziato da dict2 .

Puoi controllare: dict1 is dict2 dovrebbe essere True . Inoltre, id(dict1) dovrebbe essere lo stesso di id(dict2) .

Vuoi dict1 = copy(dict2) o dict1 = deepcopy(dict2) .

La differenza tra copy e deepcopy ? deepcopy farà in modo che gli elementi di dict2 (lo hai indicato in una lista?) siano anche copie.

Non uso molto la deepcopy - di solito è una cattiva pratica scrivere codice che ne ha bisogno (secondo me).


Puoi anche creare un nuovo dizionario con una comprensione del dizionario. Ciò evita l'importazione della copia.

dout = dict((k,v) for k,v in mydict.items())

Ovviamente in python> = 2.7 puoi fare:

dout = {k:v for k,v in mydict.items()}

Ma per retrocompatibilità, il metodo migliore è migliore.


Puoi copiare e modificare la copia appena costruita in un dict chiamando il costruttore di dict con argomenti aggiuntivi:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}

Python non copia mai implicitamente oggetti. Quando imposti dict2 = dict1 , stai facendo in modo che si riferiscano allo stesso esatto oggetto dict, quindi quando lo metti in mutamento, tutti i riferimenti a esso continuano a riferirsi all'oggetto nel suo stato corrente.

Se vuoi copiare il dict (che è raro), devi farlo esplicitamente con

dict2 = dict(dict1)

o

dict2 = dict1.copy()

Quando assegni dict2 = dict1 , non stai facendo una copia di dict1 , il risultato è in dict2 è solo un altro nome per dict1 .

Per copiare i tipi mutabili come i dizionari, usa copy / deepcopy del modulo di copy .

import copy

dict2 = copy.deepcopy(dict1)

perché, dict2 = dict1, dict2 contiene il riferimento a dict1. Sia dict1 che dict2 indicano la stessa posizione nella memoria. Questo è solo un caso normale mentre si lavora con oggetti mutabili in python. Quando lavori con oggetti mutabili in python devi fare attenzione poiché è difficile eseguire il debug. Ad esempio il seguente esempio.

 my_users = {
        'ids':[1,2],
        'blocked_ids':[5,6,7]
 }
 ids = my_users.get('ids')
 ids.extend(my_users.get('blocked_ids')) #all_ids
 print ids#output:[1, 2, 5, 6, 7]
 print my_users #output:{'blocked_ids': [5, 6, 7], 'ids': [1, 2, 5, 6, 7]}

Questa intenzione di esempio è di ottenere tutti gli ID utente inclusi gli ID bloccati. Che abbiamo ottenuto dalla variabile ids, ma abbiamo anche aggiornato il valore di my_users involontariamente. quando hai esteso gli ID con block_ids my_users è stato aggiornato perché gli ID si riferiscono a my_users .


dict1 è un simbolo che fa riferimento a un oggetto dizionario sottostante. Assegnare dict1 a dict2 assegna semplicemente lo stesso riferimento. La modifica del valore di una chiave tramite il simbolo dict2 modifica l'oggetto sottostante, che influisce anche su dict1 . Questo è confusionario.

È molto più facile ragionare su valori immutabili dei riferimenti, quindi crea copie quando è possibile:

person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26}  # does not mutate person dict

Questo è sintatticamente lo stesso di:

one_year_later = dict(person, age=26)

>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1

Ci sono molti modi per copiare l'oggetto Dict, io uso semplicemente

dict_1 = {
           'a':1,
           'b':2
         }
dict_2 = {}
dict_2.update(dict_1)

>>> x={'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}
>>> u=x.copy()
>>> v=dict(x)
>>> import copy
>>> w=copy.deepcopy(x)
>>> x['a']=10
>>> x
{'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> u
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> v
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> w
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> x['b']['m']=40
>>> x
{'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> u
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> v
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> w
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}




dictionary