Python 如何复制词典并仅编辑副本

Python 如何复制词典并仅编辑副本,python,python-3.x,dictionary,reference,Python,Python 3.x,Dictionary,Reference,有人能给我解释一下吗?这对我来说毫无意义 我把一本词典复制到另一本词典,然后编辑第二本词典,这两本词典都被修改了。为什么会这样 >>> dict1 = {"key1": "value1", "key2": "value2"} >>> dict2 = dict1 >>> dict2 {'key2': 'value2', 'key1': 'value1'} >>> dict2["key2"] = "WHY?!" >>

有人能给我解释一下吗?这对我来说毫无意义

我把一本词典复制到另一本词典,然后编辑第二本词典,这两本词典都被修改了。为什么会这样

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}
Python从不隐式复制对象。当您设置
dict2=dict1
时,您使它们引用同一个确切的dict对象,因此当您对其进行变异时,对它的所有引用都会继续引用当前状态的对象

如果您想复制dict(这种情况很少见),那么必须使用

dict2 = dict(dict1)


dict2=dict1
不复制字典。它只是让程序员有第二种方法(
dict2
)查阅同一本词典。

当您分配
dict2=dict1
时,您并不是在复制
dict1
,它导致
dict2
只是
dict1
的另一个名称

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

python中的每个变量(比如
dict1
str
\uuuuuuuuuuuuuu
都是指向机器内部某个隐藏的柏拉图式“对象”的指针

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

您可以检查:
dict1是dict2
应该是
True
。另外,
id(dict1)
应该与
id(dict2)
相同

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

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


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

这一点最初也让我感到困惑,因为我来自C语言背景

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


但在Python中,变量的行为更像是指向对象的指针。因此,将一个变量分配给另一个变量并不会产生副本,它只会使变量名指向同一个对象。

您也可以通过字典理解创建一个新的字典。这样可以避免导入副本

dout = dict((k,v) for k,v in mydict.items())
当然,在python>=2.7中,您可以执行以下操作:

dout = {k:v for k,v in mydict.items()}
但是对于向后兼容,最上面的方法更好。

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's 'b.m' property is unaffected
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
关于浅拷贝与深拷贝,请参阅:

浅复制和深复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)相关:

  • 浅复制构造一个新的复合对象,然后(尽可能)向其中插入对原始对象的引用
  • 深度副本构造一个新的复合对象,然后递归地将在原始副本中找到的对象的副本插入其中

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

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

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

因此,
dict2=dict1
,它在
dict2
dict1
引用的对象之间产生另一个绑定

如果要复制dict,可以使用
复制模块
。 复制模块有两个接口:

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

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)

深度:

无论何时执行dict2=dict1,dict2都会引用dict1。dict1和dict2都指向内存中的同一位置。在python中处理可变对象时,这只是一种正常情况。在python中处理可变对象时,必须小心,因为很难调试

您不应该使用dict2=dict1,而应该使用python的copy模块中的copy(浅拷贝)和deepcopy方法来分离dict2和dict1

正确的方法是:

dict1={“key1”:“value1”,“key2”:“value2”} >>>dict2=dict1.copy() >>>格言2 {'key1':'value1','key2':'value2'} >>>dict2[“key2”]=“为什么?” >>>格言2 {'key1':'value1','key2':'WHY?} >>>格言1 {'key1':'value1','key2':'value2'} >>>id(dict1) 140641178056312 >>>身份证(2) 140641176198960 >>> 如您所见,dict1和dict2的id都不同,这意味着它们都指向/引用内存中的不同位置

这个解决方案有效
person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26}  # does not mutate person dict
one_year_later = dict(person, age=26)
dict2 = eval(repr(dict1))
>>>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?!'}
>>>id(dict1)
 178192816

>>>id(dict2)
 178192600
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] = ..."
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'}
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'}
import copy

my_dict2 = copy.deepcopy(my_dict1)
>>> 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_1 = {
           'a':1,
           'b':2
         }
dict_2 = {}
dict_2.update(dict_1)
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = {**dict1}
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>>
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)
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}
>>> 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