15

Как скопировать словарь и редактировать только копию

13

Я создал копию словаря с помощью команды dict2 = dict1, но когда я редактирую dict2, оригинальный словарь dict1 также изменяется. Как можно избежать этого?

Пример кода:

dict1 = {"key1": "value1", "key2": "value2"}
dict2 = dict1
dict2["key2"] = "ПОЧЕМУ?!"
print(dict1)

В результате dict1 также изменяется и выглядит так: {'key2': 'ПОЧЕМУ?!', 'key1': 'value1'}. Как сделать так, чтобы изменения в dict2 не затрагивали dict1?

5 ответ(ов)

14

В Python никогда не происходит неявного копирования объектов. Когда вы пишете dict2 = dict1, вы делаете так, что оба имени ссылаются на один и тот же объект dict. Поэтому, если вы измените его, все ссылки на этот объект будут указывать на него в его текущем состоянии.

Если вы хотите создать копию словаря (что случается довольно редко), вы должны сделать это явно, используя

dict2 = dict(dict1)

или

dict2 = dict1.copy()
1

Важные моменты и простой способ запомнить:

Когда вы выполняете dict2 = dict1, переменная dict2 будет ссылаться на dict1. Оба переменные указывают на одно и то же место в памяти. Это обычная ситуация при работе с изменяемыми объектами в Python. Следует быть осторожным при работе с такими объектами, так как это может привести к трудностям в отладке.

Вместо того чтобы использовать dict2 = dict1, вам следует использовать метод copy (поверхностное копирование) или deepcopy из модуля copy, чтобы отделить dict2 от dict1.

Правильный способ сделать это:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1.copy()
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?"
>>> dict2
{'key1': 'value1', 'key2': 'WHY?'}
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>> id(dict1)
140641178056312
>>> id(dict2)
140641176198960

Как вы можете видеть, id у обоих словарей dict1 и dict2 отличаются, что означает, что они указывают на разные места в памяти.

Это решение работает для словарей с неизменяемыми значениями, но не является правильным, если значения изменяемые.

Осторожно с изменяемыми объектами в вашем словаре:

>>> import copy
>>> dict1 = {"key1": "value1", "key2": {"mutable": True}}
>>> dict2 = dict1.copy()
>>> dict2
{'key1': 'value1', 'key2': {'mutable': True}}
>>> dict2["key2"]["mutable"] = False
>>> dict2
{'key1': 'value1', 'key2': {'mutable': False}}
>>> dict1
{'key1': 'value1', 'key2': {'mutable': False}}
>>> id(dict1)
140641197660704
>>> id(dict2)
140641196407832
>>> id(dict1["key2"])
140641176198960
>>> id(dict2["key2"])
140641176198960

Вы можете заметить, что даже несмотря на то, что мы использовали copy для dict1, значение изменяемого ключа стало false в обоих словарях, хотя мы изменили его только в dict2. Это происходит потому, что мы изменяли значение изменяемого словаря, который является частью dict1. Метод .copy() по умолчанию создает поверхностную копию, что означает, что он копирует все неизменяемые значения в новый словарь, но не копирует изменяемые значения, а лишь ссылается на них.

Окончательное решение — использовать .deepcopy() для dict1, чтобы создать совершенно новый и независимый словарь со всеми скопированными внутри элементами, включая изменяемые значения.

>>> import copy
>>> dict1 = {"key1": "value1", "key2": {"mutable": True}}
>>> dict2 = copy.deepcopy(dict1)
>>> dict2
{'key1': 'value1', 'key2': {'mutable': True}}
>>> id(dict1)
140641196228824
>>> id(dict2)
140641197662072
>>> id(dict1["key2"])
140641178056312
>>> id(dict2["key2"])
140641197662000
>>> dict2["key2"]["mutable"] = False
>>> dict2
{'key1': 'value1', 'key2': {'mutable': False}}
>>> dict1
{'key1': 'value1', 'key2': {'mutable': True}}

Как видно, идентификаторы различаются, что означает, что dict2 является совершенно новым словарем, содержащим только копии элементов из dict1.

Используйте deepcopy, когда хотите изменить любое значение изменяемого объекта, не затрагивая оригинальный словарь. В противном случае вы можете использовать поверхностную копию. Однако deepcopy работает медленнее, так как он рекурсивно копирует все вложенные значения в оригинальном словаре и требует больше памяти.

0

Лучшие и простейшие способы создания копии dict как в Python 2.7, так и в 3:

Для создания копии простого (одноуровневого) словаря:

1. Используя метод dict(), вместо того чтобы создавать ссылку, указывающую на существующий словарь.

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'}

# Изменения в my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

2. Используя встроенный метод update() словаря Python.

my_dict2 = dict()
my_dict2.update(my_dict1)
print(my_dict2)  # {'message':'Hello Python'}

# Изменения в my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

Для создания копии вложенного или сложного словаря:

Используйте встроенный модуль copy, который предоставляет универсальные операции поверхностного и глубокого копирования. Этот модуль доступен как в Python 2.7, так и в 3.

import copy

my_dict2 = copy.deepcopy(my_dict1)
0

Вы также можете создать новый словарь с помощью генератора словаря. Это позволяет избежать импорта модуля copy.

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

Однако, начиная с Python ≥ 2.7, можно использовать более удобный синтаксис:

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

Но для обеспечения обратной совместимости первый метод будет предпочтительнее.

0

В Python оператор присваивания не копирует объекты, а создает привязки между целевым именем и объектом.

Таким образом, когда вы выполняете dict2 = dict1, вы создаете новую привязку между dict2 и объектом, на который ссылается dict1.

Если вы хотите создать копию словаря, вы можете воспользоваться модулем copy. Этот модуль предоставляет два интерфейса:

copy.copy(x)
# Возвращает поверхностную копию x.

copy.deepcopy(x)
# Возвращает глубокую копию x.

Разница между поверхностным и глубоким копированием имеет значение только для составных объектов (объектов, которые содержат другие объекты, такие как списки или экземпляры классов):

  • Поверхностная копия создает новый составной объект, а затем (насколько это возможно) вставляет в него ссылки на объекты, найденные в оригинале.

  • Глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.

Например, в 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')

В результате получится следующее:

>>> 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']]

Как видно из примера, изменение a также изменяет b (поскольку b является ссылкой на a), тогда как изменения c и d не затрагивают a, что связано с тем, что c является поверхностной копией, а d — глубокой.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь