如何复制字典并仅编辑副本

时间:2010-03-17 21:07:06

标签: python python-3.x dictionary reference

有人可以向我解释一下吗?这对我没有任何意义。

我将字典复制到另一个字典中并编辑第二个字典,并且两者都被更改。为什么会这样?

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}

23 个答案:

答案 0 :(得分:711)

Python 从不隐式复制对象。当你设置dict2 = dict1时,你正在使它们引用相同的dict对象,所以当你改变它时,对它的所有引用都会引用当前状态的对象。

如果要复制dict(很少见),则必须使用

明确地执行此操作
dict2 = dict(dict1)

dict2 = dict1.copy()

答案 1 :(得分:478)

当您指定dict2 = dict1时,您没有复制dict1,导致dict2只是dict1的另一个名称。

要复制字典等可变类型,请使用copy模块的copy / deepcopy

import copy

dict2 = copy.deepcopy(dict1)

答案 2 :(得分:125)

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

答案 3 :(得分:58)

在python 3.5+上,使用** unpackaging运算符可以更轻松地实现浅拷贝。由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?!'}

**将字典解压缩到一个新的字典中,然后分配给dict2。

我们还可以确认每个字典都有一个不同的id。

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600

如果需要深层副本,那么copy.deepcopy()仍然是可行的方法。

答案 4 :(得分:35)

您也可以使用词典理解来创建一个新词典。这样可以避免导入副本。

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

当然在python> = 2.7中你可以这样做:

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

但是对于向后比较,顶级方法更好。

答案 5 :(得分:33)

Python 2.7和3 创建 dict 的最佳和最简单方法是...

创建简单(单级)字典的副本:

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

# Made changes in my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

2. 使用python字典的内置 update()方法。

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

创建嵌套或复杂字典的副本:

使用内置的复制模块,该模块提供通用的浅层和深层复制操作。该模块存在于Python 2.7和3。*

import copy

my_dict2 = copy.deepcopy(my_dict1)

答案 6 :(得分:16)

Python中的赋值语句不复制对象,它们在目标和对象之间创建绑定。

所以,dict2 = dict1会导致dict2dict1引用的对象之间的另一个绑定。

如果要复制字典,可以使用copy module。 复制模块有两个接口:

copy.copy(x)
Return a shallow copy of x.

copy.deepcopy(x)
Return a deep copy of 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']]

答案 7 :(得分:15)

除了提供的其他解决方案外,您还可以使用**将字典集成到空字典中,例如

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

Pointer: Difference between shallow and deep copys

答案 8 :(得分:10)

您可以通过使用其他关键字参数调用dict构造函数来一次复制和编辑新构建的副本:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}

答案 9 :(得分:9)

最初,这让我很困惑,因为我来自C背景。

在C中,变量是内存中具有已定义类型的位置。分配给变量会将数据复制到变量的内存位置。

但在Python中,变量更像是指向对象的指针。因此,将一个变量分配给另一个变量不会复制,只是使该变量名称指向同一个对象。

答案 10 :(得分:7)

python中的每个变量(dict1str__builtins__之类的东西都是指向机器内部隐藏的柏拉图“对象”的指针。

如果设置dict1 = dict2,则只需将dict1指向与dict2相同的对象(或内存位置,或您喜欢的任何类比)。现在,dict1引用的对象与dict2引用的对象相同。

您可以查看:dict1 is dict2应为True。此外,id(dict1)应与id(dict2)相同。

您需要dict1 = copy(dict2)dict1 = deepcopy(dict2)

copydeepcopy之间的区别? deepcopy会确保dict2的元素(您是否将其指向列表?)也是副本。

我不太使用deepcopy - 编写需要它的代码通常是不好的做法(在我看来)。

答案 11 :(得分:6)

dict2 = dict1不会复制字典。它只是让程序员以第二种方式(dict2)来引用相同的字典。

答案 12 :(得分:5)

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)

答案 13 :(得分:3)

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

答案 14 :(得分:1)

很好的解释,我想添加在考虑与=相等地分配的Python变量时可以参考的最简单的规则。如果数据类型是不可变的,则不必担心遇到的意外行为。如果数据类型是可变的,则要确保对其进行复制,以防止遇到意外行为。

不可更改的数据类型:字符串(字符元组),元组

可变数据类型:列表,数组,字典

答案 15 :(得分:1)

正如其他人所解释的那样,内置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] = ..."

请参阅此处讨论的左值修改模式:Python 2.7 - clean syntax for lvalue modification。关键的观察结果是strint在Python中表现为值(即使它们实际上是不可变的对象)。在您进行观察的同时,请注意strint没有什么神奇之处。 dict可以以相同的方式使用,我可以想到ValueDict有意义的许多情况。

答案 16 :(得分:1)

因为python使用引用,所以当你执行dict2 = dict1时,你传递了对dict2的引用,这与dict1相同。因此,当您在dict1或dict2中进行更改时,您会更改引用,并且两者都会更改。对不起,如果我对英语有误。

答案 17 :(得分:0)

在尝试深度复制不分配给变量的类的字典属性时,我遇到了一种特殊的行为

new = copy.deepcopy(my_class.a)不起作用,即修改new会修改my_class.a

但是如果您先执行old = my_class.a然后再执行new = copy.deepcopy(old),则它可以正常运行,即修改new不会影响my_class.a

我不确定为什么会这样,但是希望它可以节省一些时间! :)

答案 18 :(得分:0)

另一种更清洁的方法是使用json。参见下面的代码

>>> a = [{"name":"Onkar","Address": {"state":"MH","country":"India","innerAddress":{"city":"Pune"}}}]
>>> b = json.dumps(a)
>>> b = json.loads(b)
>>> id(a)
2334461105416
>>> id(b)
2334461105224
>>> a[0]["Address"]["innerAddress"]["city"]="Nagpur"
>>> a
[{'name': 'Onkar', 'Address': {'state': 'MH', 'country': 'India', 'innerAddress': {'city': 'Nagpur'}}}]
>>> b
[{'name': 'Onkar', 'Address': {'state': 'MH', 'country': 'India', 'innerAddress': {'city': 'Pune'}}}]
>>> id(a[0]["Address"]["innerAddress"])
2334460618376
>>> id(b[0]["Address"]["innerAddress"])
2334424569880

要创建另一个字典,请在同一字典对象上执行json.dumps(),然后执行json.loads()。您将有单独的dict对象。

答案 19 :(得分:0)

以下代码是在字典上,它遵循json语法的速度是Deepcopy的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 :(得分:0)

因为,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]}

此示例意图是获取所有用户ID,包括阻止的ID。 我们从ids变量获得但我们也无意中更新了 my_users 的值。当您使用 blocked_ids 扩展 ID 时,my_users已更新,因为 ID 请参阅 my_users

答案 21 :(得分:-1)

使用for循环进行复制:

orig = {"X2": 674.5, "X3": 245.0}

copy = {}
for key in orig:
    copy[key] = orig[key]

print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 674.5, 'X3': 245.0}
copy["X2"] = 808
print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 808, 'X3': 245.0}

答案 22 :(得分:-6)

您可以直接使用:

dict2 = eval(repr(dict1))

其中对象dict2是dict1的独立副本,因此您可以在不影响dict1的情况下修改dict2。

这适用于任何类型的物体。