Как скопировать словарь и только отредактировать копию
Может кто-нибудь объяснит мне это? Это не имеет никакого смысла для меня.
Я копирую словарь в другой и редактирую второй, и оба они меняются. Почему это происходит?
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}
Ответы
Ответ 1
Python никогда не копирует объекты неявно. Когда вы устанавливаете dict2 = dict1
, вы заставляете их ссылаться на один и тот же точный объект dict, поэтому, когда вы его мутируете, все ссылки на него продолжают ссылаться на объект в его текущем состоянии.
Если вы хотите скопировать dict (что редко), вы должны сделать это явно с помощью
dict2 = dict(dict1)
или
dict2 = dict1.copy()
Ответ 2
Когда вы назначаете dict2 = dict1
, вы не делаете копию dict1
, это приводит к тому, что dict2
является просто другим именем для dict1
.
Чтобы скопировать изменяемые типы, например словари, используйте copy
/deepcopy
модуля copy
.
import copy
dict2 = copy.deepcopy(dict1)
Ответ 3
Хотя dict.copy()
и dict(dict1)
генерируют копию, они являются только мелкими копиями. Если вам нужна глубокая копия, требуется copy.deepcopy(dict1)
. Пример:
>>> source = {'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}
>>> copy1 = x.copy()
>>> copy2 = dict(x)
>>> import copy
>>> copy3 = copy.deepcopy(x)
>>> source['a'] = 10 # a change to first-level properties won't affect copies
>>> source
{'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy3
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> source['b']['m'] = 40 # a change to deep properties WILL affect shallow copies 'b.m' property
>>> source
{'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy3 # Deep copy 'b.m' property is unaffected
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
Что касается мелких и глубоких копий, из документации по модулю Python copy
:
Различие между мелким и глубоким копированием относится только к составным объектам (объектам, которые содержат другие объекты, например списки или экземпляры классов):
- Мелкая копия создает новый составной объект, а затем (насколько это возможно) вставляет в него ссылки на объекты, найденные в оригинале.
- Глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.
Ответ 4
На python 3. 5+ есть более простой способ получить мелкую копию, используя ** оператор распаковки. Определено Пепом 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?!'}
** распаковывает словарь в новый словарь, который затем назначается dict2.
Мы также можем подтвердить, что каждый словарь имеет отдельный идентификатор.
>>>id(dict1)
178192816
>>>id(dict2)
178192600
Если вам нужна глубокая копия, тогда copy.deepcopy() все еще остается подходящим вариантом.
Ответ 5
Лучший и самый простой способ создать копию dict в Python 2.7 и 3 - это...
Чтобы создать копию простого (одноуровневого) словаря:
1. Использование метода dict() вместо создания ссылки, указывающей на существующий 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'}
# Made changes in 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'}
# Made changes in 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)
Ответ 6
Вы также можете просто создать новый словарь с пониманием словаря. Это позволяет избежать импорта копии.
dout = dict((k,v) for k,v in mydict.items())
Конечно, в python >= 2.7 вы можете сделать:
dout = {k:v for k,v in mydict.items()}
Но для обратной совместимости лучший способ лучше.
Ответ 7
В дополнение к другим предоставленным решениям вы можете использовать **
для интеграции словаря в пустой словарь, например,
shallow_copy_of_other_dict = {**other_dict}
.
Теперь у вас будет "мелкая" копия other_dict
.
Применимо к вашему примеру:
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = {**dict1}
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>>
Указатель: разница между мелкими и глубокими копиями
Ответ 8
Операторы присваивания в Python не копируют объекты, они создают привязки между объектом и объектом.
so, dict2 = dict1
, он вызывает другое связывание между dict2
и объектом, к которому относится dict1
.
если вы хотите скопировать dict, вы можете использовать copy module
.
Модуль копирования имеет два интерфейса:
copy.copy(x)
Return a shallow copy of x.
copy.deepcopy(x)
Return a deep copy of x.
Разница между мелким и глубоким копированием применима только для составных объектов (объектов, которые содержат другие объекты, например списки или экземпляры классов):
A мелкая копия создает новый составной объект и затем (насколько это возможно) вставляет ссылки в него в объекты, найденные в оригинале.
A глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.
Например, в 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']]
Ответ 9
Вы можете скопировать и отредактировать недавно созданную копию за один проход, вызвав конструктор dict
с дополнительными аргументами ключевого слова:
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}
Ответ 10
Это тоже смутило меня, потому что я исходил из фона C.
В C переменная представляет собой местоположение в памяти с определенным типом. Назначение переменной копирует данные в ячейку переменной памяти.
Но в Python переменные действуют скорее как указатели на объекты. Поэтому присвоение одной переменной другой не создает копию, она просто превращает это имя переменной в один и тот же объект.
Ответ 11
Каждая переменная в python (например, dict1
или str
или __builtins__
является указателем на некоторый скрытый платонический "объект" внутри машины.
Если вы установили dict1 = dict2
, вы просто указываете dict1
на тот же объект (или ячейку памяти или любую другую аналогию) как dict2
. Теперь объект, на который ссылается dict1
, является тем же объектом, на который ссылается dict2
.
Вы можете проверить: dict1 is dict2
должен быть True
. Кроме того, id(dict1)
должен быть таким же, как id(dict2)
.
Вы хотите dict1 = copy(dict2)
или dict1 = deepcopy(dict2)
.
Разница между copy
и deepcopy
? deepcopy
будет убедиться, что элементы dict2
(вы указали его в списке?) также являются копиями.
Я не использую deepcopy
много - обычно это плохая практика писать код, который ему нужен (на мой взгляд).
Ответ 12
dict2 = dict1
не копирует словарь. Это просто дает программисту второй способ (dict2
), чтобы ссылаться на тот же словарь.
Ответ 13
dict1
- это символ, который ссылается на базовый объект словаря. Присвоение dict1
на dict2
просто присваивает ту же ссылку. Изменение значения ключа с помощью символа dict2
изменяет базовый объект, что также влияет на dict1
. Это запутывает.
Намного легче рассуждать о неизменяемых значениях, чем ссылки, поэтому делайте копии по возможности:
person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26} # does not mutate person dict
Это синтаксически то же самое, что:
one_year_later = dict(person, age=26)
Ответ 14
>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1
Есть много способов скопировать объект Dict, я просто использую
dict_1 = {
'a':1,
'b':2
}
dict_2 = {}
dict_2.update(dict_1)
Ответ 15
Поскольку python работает со ссылкой, поэтому, когда вы сделали dict2 = dict1, вы передаете ссылку на dict2, это было то же самое, что и dict1. Итак, когда вы вносите изменения в dict1 или dict2, вы меняете ссылку, и оба определяют chages. Извините, если я что-то ошибся на английском.
Ответ 16
Как объяснили другие, встроенный dict
не делает то, что вы хотите. Но в Python2 (и, вероятно, 3 тоже) вы можете легко создать класс ValueDict
, который копирует с помощью =
, чтобы вы могли быть уверены, что оригинал не изменится.
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] = ..."
Пожалуйста, обратитесь к шаблону модификации lvalue, обсуждаемому здесь: Python 2.7 - чистый синтаксис для изменения lvalue. Главное наблюдение заключается в том, что str
и int
ведут себя как значения в Python (хотя они фактически являются неизменяемыми объектами под капотом). В то время как вы это замечаете, также обратите внимание, что ничего особенного в str
или int
нет. dict
можно использовать почти одинаково, и я могу думать о многих случаях, когда ValueDict
имеет смысл.
Ответ 17
Хорошие объяснения, я хочу добавить простейшее правило, на которое вы можете ссылаться при рассмотрении переменных Python, которые вы назначаете равными =
. Если тип данных является неизменяемым, вам не нужно беспокоиться о неожиданном поведении, с которым вы столкнулись. Если тип данных изменчив, вы должны обязательно сделать его копию, чтобы предотвратить непредвиденное поведение, с которым вы столкнулись.
Неизменяемые типы данных: строка (кортеж символов), кортеж
Изменяемые типы данных: список, массив, словарь
Ответ 18
потому что dict2 = dict1, dict2 содержит ссылку на dict1. Оба dict1 и dict2 указывают на то же место в памяти. Это обычный случай при работе с изменяемыми объектами в python. Когда вы работаете с изменяемыми объектами в python, вы должны быть осторожны, поскольку его трудно отлаживать. Например, следующий пример.
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]}
Это примерное намерение состоит в том, чтобы получить все идентификаторы пользователей, включая заблокированные идентификаторы.
Это мы получили от переменной ids, но мы также непреднамеренно обновили значение my_users. при расширении идентификаторов с заблокированными_имя my_users обновлены, так как Иды относятся к my_users.
Ответ 19
следующий код, который содержит слова, которые следуют синтаксису json более чем в 3 раза быстрее, чем глубокая копия
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)
Ответ 20
Вы можете использовать напрямую:
dict2 = eval(repr(dict1))
где object dict2 является независимой копией dict1, поэтому вы можете изменить dict2, не затрагивая dict1.
Это работает для любого объекта.