Comment copier un dictionnaire et seulement éditer la copie
quelqu'un peut-il me l'expliquer? Ca n'a aucun sens pour moi.
je copie un dictionnaire dans un autre et édite le second et les deux sont changés. Pourquoi est-ce arrivé?
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}
17 réponses
Python never copie implicitement des objets. Lorsque vous définissez dict2 = dict1
, vous les faites se référer au même objet dict exact, donc quand vous le mutez, toutes les références à lui continuent de se référer à l'objet dans son état actuel.
si vous voulez copier le dict (ce qui est rare), vous devez le faire explicitement avec
dict2 = dict(dict1)
ou
dict2 = dict1.copy()
quand vous assignez dict2 = dict1
, vous ne faites pas une copie de dict1
, il en résulte que dict2
est juste un autre nom pour dict1
.
pour copier les types mutables comme les dictionnaires, utilisez copy
/ deepcopy
du module copy
.
import copy
dict2 = copy.deepcopy(dict1)
>>> 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}}
sur python 3.5+ il y a un moyen plus facile d'obtenir une copie superficielle en utilisant l'opérateur ** unpackaging. Défini par Pep 448 .
>>>dict1 = {"key1": "value1", "key2": "value2"}
>>>dict2 = {**dict1}
>>>print(dict2)
{'key1': 'value1', 'key2': 'value2'}
>>>dict2["key2"] = "WHY?!"
>>>print(dict1)
{'key1': 'value1', 'key2': 'value2'}
>>>print(dict2)
{'key1': 'value1', 'key2': 'WHY?!'}
* * déballage du dictionnaire dans un nouveau dictionnaire qui est ensuite assigné à dict2.
nous pouvons également confirmer que chaque dictionnaire a une identification distincte.
>>>id(dict1)
178192816
>>>id(dict2)
178192600
si une copie profonde est nécessaire alors copie.propriétédeepcopy() est encore du chemin à parcourir.
vous pouvez également juste faire un nouveau dictionnaire avec une compréhension de dictionnaire. Cela évite l'importation de copie.
dout = dict((k,v) for k,v in mydict.items())
bien sûr en python > = 2.7 vous pouvez faire:
dout = {k:v for k,v in mydict.items()}
Mais pour l'arrière compat. le top méthode est la meilleure.
les instructions D'affectation en Python ne copient pas les objets, elles créent des liaisons entre une cible et un objet.
ainsi, dict2 = dict1
, il en résulte une autre liaison entre dict2
et l'objet auquel dict1
se réfère.
si vous voulez copier un dict, vous pouvez utiliser le copy module
.
Le module de copie a deux interfaces:
copy.copy(x)
Return a shallow copy of x.
copy.deepcopy(x)
Return a deep copy of x.
la différence entre copie superficielle et copie profonde n'est pertinente que pour objets composés (objets qui contiennent d'autres objets, comme des listes ou des instances de classe):
Un copie construit un nouvel objet composé, puis (dans la mesure du possible) insère des références pour les objets trouvés dans l'original.
Un copie construit un nouvel objet composé et puis, de manière récursive, insère des copies en elle de la les objets trouvés dans l'original.
par exemple, en 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')
et le résultat est:
>>> 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']]
la meilleure et la plus facile des façons de créer une copie d'un dict dans les deux Python 2.7 et 3 sont...
pour créer une copie du dictionnaire simple(à un seul niveau):
1. utilisant la méthode dict () , au lieu de générer une référence qui pointe vers le dict existant.
my_dict1 = dict()
my_dict1["message"] = "Hello Python"
print(my_dict1) # {'message':'Hello Python'}
my_dict2 = dict(my_dict1)
print(my_dict2) # {'message':'Hello Python'}
# Made changes in my_dict1
my_dict1["name"] = "Emrit"
print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2) # {'message':'Hello Python'}
2. intégré update() méthode de python dictionnaire.
my_dict2 = dict()
my_dict2.update(my_dict1)
print(my_dict2) # {'message':'Hello Python'}
# Made changes in my_dict1
my_dict1["name"] = "Emrit"
print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2) # {'message':'Hello Python'}
pour créer une copie du dictionnaire emboîté ou complexe:
utilise le module intégré copie , qui fournit une opération générique de copie peu profonde et profonde. Ce module est présent en Python 2.7 et 3.*
import copy
my_dict2 = copy.deepcopy(my_dict1)
vous pouvez copier et éditer la copie nouvellement construite en une seule fois en appelant le constructeur dict
avec des arguments de mots clés supplémentaires:
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}
toute variable en python (des trucs comme dict1
ou str
ou __builtins__
est un pointeur vers un "objet" platonique caché à l'intérieur de la machine.
si vous mettez dict1 = dict2
, vous pointez juste dict1
vers le même objet (ou emplacement de mémoire, ou n'importe quelle analogie que vous aimez) comme dict2
. Maintenant, l'objet référencé par dict1
est le même objet référencé par dict2
.
vous pouvez vérifier: dict1 is dict2
devrait être True
. En outre, id(dict1)
devrait être le même que id(dict2)
.
Vous voulez dict1 = copy(dict2)
, ou dict1 = deepcopy(dict2)
.
la différence entre copy
et deepcopy
? deepcopy
s'assurera que les éléments de dict2
(l'avez-vous pointé sur une liste?) sont également des copies.
je n'utilise pas deepcopy
beaucoup - c'est généralement une mauvaise pratique d'écrire du code qui en a besoin (à mon avis).
cela m'a embrouillé aussi, d'abord, parce que je venais d'un arrière-plan en C.
En C, une variable est un emplacement dans la mémoire avec un type défini. Attribuer à une variable copie les données dans l'emplacement de la mémoire de la variable.
mais en Python, les variables agissent plus comme des pointeurs vers des objets. Ainsi, attribuer une variable à une autre ne fait pas de copie, Cela fait simplement pointer le nom de la variable sur le même objet.
dict1
est un symbole qui renvoie à un objet sous-jacent du dictionnaire. L'affectation de dict1
à dict2
ne fait qu'attribuer la même référence. Changer la valeur d'une clé via le symbole dict2
change l'objet sous-jacent, ce qui affecte aussi dict1
. Ceci est source de confusion.
il est beaucoup plus facile de raisonner sur des valeurs immuables que des références, donc faites des copies chaque fois que possible:
person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26} # does not mutate person dict
c'est syntaxiquement le identique à:
one_year_later = dict(person, age=26)
parce que python fonctionne avec référence, donc quand vous avez fait dict2 = dict1 vous passez une référence à dict2, qui était la même que dict1. Donc, quand vous faites un changement dans dict1 ou dict2 vous changez une référence, et les deux dicts chantent. Désolé si je me trompe quelque chose sur l'anglais.
Comme d'autres l'ont expliqué, le dict
ne pas faire ce que vous voulez. Mais en Python2 (et probablement 3 Aussi) vous pouvez facilement créer une classe ValueDict
qui copie avec =
de sorte que vous pouvez être sûr que l'original ne changera pas.
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] = ..."
veuillez vous référer au modèle de modification de lvalue discuté ici: Python 2.7 - syntaxe clean pour la modification de lvalue . La principale observation est que str
et int
se comportent comme des valeurs en Python (même si ce sont en fait des objets immuables sous le capot). Pendant que vous observez cela, s'il vous plaît également noter que rien n'est magiquement spécial au sujet de str
ou int
. dict
peut être utilisé de la même manière, et je peux penser à de nombreux cas où ValueDict
a un sens.
parce que, dict2 = dict1, dict2 tient la référence à dict1. Dict1 et dict2 pointent tous deux vers le même endroit dans la mémoire. C'est juste un cas normal en travaillant avec des objets mutables en python. Lorsque vous travaillez avec des objets mutables en python, vous devez être prudent, car il est difficile à déboguer. Comme dans l'exemple suivant.
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]}
Cet exemple, l'intention est d'obtenir tous les id d'utilisateur, y compris bloqué id. Que nous avons obtenu d'ids variable mais nous avons aussi mis à jour la valeur de my_users involontairement. lors de l'extension de la id avec blocked_ids my_users s'est mis à jour parce que id consultez my_users .
>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1
il y a plusieurs façons de copier un objet Dict, j'utilise simplement
dict_1 = {
'a':1,
'b':2
}
dict_2 = {}
dict_2.update(dict_1)
vous pouvez utiliser directement:
dict2 = eval(repr(dict1))
où objet dict2 est une copie indépendante de dict1, donc vous pouvez modifier dict2 sans affecter dict1.
cela fonctionne pour tout type d'objet.