Quelqu'un pourrait-il me l'expliquer? Cela n'a aucun sens pour moi.
Je copie un dictionnaire dans un autre et édite le second et les deux sont modifiés. Pourquoi cela arrive-t-il?
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}
Python jamais copie implicitement les objets. Lorsque vous définissez dict2 = dict1
, vous les faites se référer au même objet dict exact. Par conséquent, lorsque vous le modifiez, toutes les références à celui-ci continuent à faire référence à 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()
Lorsque vous attribuez dict2 = dict1
, vous ne copiez pas dict1
, dict2
n'est qu'un autre nom pour dict1
.
Pour copier les types modifiables tels que 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 existe un moyen plus simple de réaliser une copie superficielle à l'aide de l'opérateur ** de dépaquetage. 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écompresse le dictionnaire dans un nouveau dictionnaire qui est ensuite attribué à dict2.
Nous pouvons également confirmer que chaque dictionnaire a un identifiant distinct.
>>>id(dict1)
178192816
>>>id(dict2)
178192600
Si une copie en profondeur est nécessaire, alors copy.deepcopy () est toujours le chemin à parcourir.
Les meilleurs et les plus faciles moyens de créer une copie d'un dict dans les deux Python 2.7 et = sont ...
Pour créer une copie du dictionnaire simple (à un niveau):
1. Utiliser 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. Utilisation de la méthode intégrée pdate () du dictionnaire python.
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 imbriqué ou complexe:
Utilisez le module copie intégré, qui fournit une copie générique des opérations de copie superficielle et profonde. Ce module est présent dans Python 2.7 et 3. *
import copy
my_dict2 = copy.deepcopy(my_dict1)
Vous pouvez également créer un nouveau dictionnaire avec une compréhension du dictionnaire. Cela évite d'importer une copie.
dout = dict((k,v) for k,v in mydict.items())
Bien sûr, dans python> = 2.7, vous pouvez faire:
dout = {k:v for k,v in mydict.items()}
Mais pour les compatriotes, la méthode la plus efficace est préférable.
Les instructions d'assignation dans Python ne copient pas les objets, elles créent des liaisons entre une cible et un objet.
alors, dict2 = dict1
, il en résulte une nouvelle liaison entre dict2
et l'objet auquel dict1
fait référence.
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 la copie superficielle et profonde ne concerne que les objets composés (objets contenant d'autres objets, tels que des listes ou des instances de classe):
Une copie superficielle construit un nouvel objet composé et y insère (dans la mesure du possible) des références aux objets trouvés dans l'original.
Une copie profonde construit un nouvel objet composé et y insère ensuite, de manière récursive, des copies des objets trouvés dans l'original.
Par exemple, dans 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']]
Outre les autres solutions fournies, vous pouvez utiliser _**
_ pour intégrer le dictionnaire dans un dictionnaire vide, par exemple,
shallow_copy_of_other_dict = {**other_dict}
.
Maintenant, vous aurez une copie "superficielle" de _other_dict
_.
Appliqué à votre exemple:
_>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = {**dict1}
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>>
_
Pointeur: Différence entre les copies peu profondes et profondes
Vous pouvez copier et éditer la copie nouvellement construite en une 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'}
Cela m’a confondu aussi, au début, parce que je venais d’un milieu C.
En C, une variable est un emplacement en mémoire avec un type défini. L'affectation à une variable copie les données dans l'emplacement mémoire de la variable.
Mais en Python, les variables agissent davantage comme des pointeurs sur des objets. Donc, assigner une variable à une autre ne crée pas de copie, cela fait simplement que le nom de la variable pointe vers le même objet.
Chaque variable de python (un objet comme dict1
ou str
ou __builtins__
est un pointeur sur un "objet" platonique masqué dans la machine.
Si vous définissez dict1 = dict2
, il vous suffit de pointer dict1
vers le même objet (ou emplacement mémoire, ou selon l'analogie de votre choix) en tant que 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
. De plus, id(dict1)
devrait être identique à 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 dirigée vers une liste?) Sont également des copies.
Je n'utilise pas beaucoup deepcopy
- c'est généralement une mauvaise pratique d'écrire du code qui en a besoin (à mon avis).
dict2 = dict1
ne copie pas le dictionnaire. Cela donne simplement au programmeur un second moyen (dict2
) de se référer au même dictionnaire.
dict1
est un symbole qui fait référence à un objet de dictionnaire sous-jacent. L'attribution de dict1
à dict2
affecte simplement la même référence. La modification de la valeur d'une clé à l'aide du symbole dict2
modifie l'objet sous-jacent, ce qui affecte également dict1
. Ceci est déroutant.
Il est beaucoup plus facile de raisonner sur les valeurs immuables que sur les références, alors faites des copies autant que possible:
person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26} # does not mutate person dict
Ceci est syntaxiquement identique à:
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
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)
Comme d'autres l'ont expliqué, la dict
intégrée ne fait pas ce que vous voulez. Mais en Python2 (et probablement 3 aussi), vous pouvez facilement créer une classe ValueDict
qui copie avec =
afin que vous puissiez ê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 reporter au modèle de modification de lvalue décrit ici: Python 2.7 - syntaxe propre pour la modification de lvalue . L'observation clé est que str
et int
se comportent comme des valeurs dans Python (même s'il s'agit d'objets immuables sous le capot). Pendant que vous observez cela, veuillez également noter que rien n’a de magie particulière sur str
ou int
. dict
peut être utilisé à peu près de la même manière, et je peux penser à de nombreux cas où ValueDict
a un sens.
De bonnes explications, je veux ajouter la règle la plus simple à laquelle vous pouvez vous référer lorsque vous pensez à Python variables que vous affectez égales à =
. Si le type de données est immuable, vous n'avez pas à vous soucier du comportement inattendu que vous avez rencontré. Si le type de données est modifiable, vous voulez vous assurer de le copier pour éviter le comportement inattendu que vous avez rencontré.
types de données immuables: chaîne (un tuple de caractères), tuple
types de données modifiables: liste, tableau, dictionnaire
Étant donné que python fonctionne avec une référence, si vous avez dict2 = dict1, vous transmettez une référence à dict2, ce qui était identique à dict1. Ainsi, lorsque vous apportez une modification à dict1 ou dict2, vous modifiez une référence et les modifications des deux dictic. Désolé si je confonds quelque chose sur l'anglais.
Juste pour les enregistrements. J'utilise le code suivant, qui est sur dicts et qui suit la syntaxe json plus de 3 fois plus vite que deepcopy
def CopyDict(dSrc):
try:
return json.loads(json.dumps(dSrc))
except Exception as e:
Logger.warning("Can't copy dict the preferred way:"+str(dSrc))
return deepcopy(dSrc)
parce que, dict2 = dict1, dict2 contient la référence à dict1. Dict1 et dict2 pointent au même endroit dans la mémoire. Ceci est juste un cas normal lorsque vous travaillez avec des objets mutables en python. Lorsque vous travaillez avec des objets mutables dans python, vous devez faire attention car il est difficile de déboguer. Tels que 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 vise à obtenir tous les ID utilisateur, y compris les ID bloqués. Cela provient de la variable ids mais nous avons également mis à jour la valeur de my_users involontairement. lorsque vous avez étendu les ids avec block_ids mes_utilisateurs ont été mis à jour car ids se réfèrent à mes_utilisateurs .