Как скопировать словарь и редактировать только копию
Я создал копию словаря с помощью команды dict2 = dict1
, но когда я редактирую dict2
, оригинальный словарь dict1
также изменяется. Как можно избежать этого?
Пример кода:
dict1 = {"key1": "value1", "key2": "value2"}
dict2 = dict1
dict2["key2"] = "ПОЧЕМУ?!"
print(dict1)
В результате dict1
также изменяется и выглядит так: {'key2': 'ПОЧЕМУ?!', 'key1': 'value1'}
. Как сделать так, чтобы изменения в dict2
не затрагивали dict1
?
5 ответ(ов)
В Python никогда не происходит неявного копирования объектов. Когда вы пишете dict2 = dict1
, вы делаете так, что оба имени ссылаются на один и тот же объект dict. Поэтому, если вы измените его, все ссылки на этот объект будут указывать на него в его текущем состоянии.
Если вы хотите создать копию словаря (что случается довольно редко), вы должны сделать это явно, используя
dict2 = dict(dict1)
или
dict2 = dict1.copy()
Важные моменты и простой способ запомнить:
Когда вы выполняете 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
работает медленнее, так как он рекурсивно копирует все вложенные значения в оригинальном словаре и требует больше памяти.
Лучшие и простейшие способы создания копии 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)
Вы также можете создать новый словарь с помощью генератора словаря. Это позволяет избежать импорта модуля copy
.
dout = dict((k, v) for k, v in mydict.items())
Однако, начиная с Python ≥ 2.7, можно использовать более удобный синтаксис:
dout = {k: v for k, v in mydict.items()}
Но для обеспечения обратной совместимости первый метод будет предпочтительнее.
В 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
— глубокой.
Итерация по словарям с использованием циклов 'for'
Создание словаря (dict) из отдельных списков ключей и значений
Преобразование списка словарей в DataFrame pandas
Почему использовать dict.get(key) вместо dict[key]?
Получить ключ по значению в словаре