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

Может кто-нибудь объяснит мне это? Это не имеет никакого смысла для меня.

Я копирую словарь в другой и редактирую второй, и оба они меняются. Почему это происходит?

>>> 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.

Это работает для любого объекта.