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

2024-11-21 08:33:00
admin
原创
5
摘要:问题描述:我设置了dict2 = dict1。当我编辑时dict2,原始内容dict1也会更改。我该如何避免这种情况?>>> dict1 = {"key1": "value1", "key2": "value2"...

问题描述:

我设置了dict2 = dict1。当我编辑时dict2,原始内容dict1也会更改。我该如何避免这种情况?

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

解决方案 1:

Python从不隐式复制对象。当您设置时dict2 = dict1,您让它们引用完全相同的 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 = source.copy()
>>> copy2 = dict(source)
>>> import copy
>>> copy3 = copy.deepcopy(source)
>>> 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}}

关于浅拷贝和深拷贝,来自Pythoncopy模块文档:

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

  • 浅拷贝构造一个新的复合对象,然后(在可能的范围内)将对原始对象的引用插入到其中。

  • 深层复制构造一个新的复合对象,然后以递归方式将原始对象的副本插入其中。

解决方案 4:

深入了解并轻松记忆的方法:

每当你做 时dict2 = dict1,都dict2指的是dict1dict1dict2都指向内存中的同一位置。 这只是在 Python 中使用可变对象时的正常情况。 在 Python 中使用可变对象时,你必须小心,因为它很难调试。

您不应该使用dict2 = dict1,而应该使用python 的copy 模块copy中的(浅拷贝) 或方法来与分离。deepcopydict2`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
>>> 

如您所见,两者的iddict1dict2不同,这意味着它们指向/引用内存中的不同位置。

此解决方案适用于具有不可变值的字典,但对于具有可变值的字典来说,它不是正确的解决方案。

请注意字典中的可变变量:

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

您可以看到,尽管我们对 进行了复制dict1,但 mutable 的值在 和 上都更改为 false dict2dict1尽管我们仅在 上对其进行了更改dict2。这是因为我们更改了 的可变字典部分的值dict1。当我们.copy()对 字典执行 时,默认情况下,它只会执行浅拷贝,这意味着它将所有不可变值复制到新字典中,但不会复制 可变值,而只会引用它们。

最终的解决方案是创建.deepycopy()一个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}}

正如您所见,id 是不同的,这意味着这dict2是一个全新的字典,其中仅包含来自 的项目的副本dict1

每当您想要更改任何可变值而不影响原始字典时,都必须使用 Deepcopy。如果不行,您可以使用浅拷贝。Deepcopy 速度很慢,因为它以递归方式复制原始字典中的任何嵌套值,并且还会占用额外的内存。

解决方案 5:

在 Python 3.5+ 中,有一种更简单的方法来实现浅拷贝,即使用 ** 解包运算符。由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()仍然是可行的方法。

解决方案 6:

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

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

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

使用内置的copy模块,它提供了通用的浅层和深层复制操作。此模块在 Python 2.7 和 3 中均有提供。*

import copy

my_dict2 = copy.deepcopy(my_dict1)

解决方案 7:

您也可以使用字典推导式创建新字典。这样可以避免导入副本。

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

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

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

但为了向后兼容,顶部方法更好。

解决方案 8:

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

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

指针:浅拷贝和深拷贝的区别

解决方案 9:

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

因此,它导致了和所引用的对象dict2 = dict1之间的另一种绑定。dict2`dict1`

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

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

解决方案 10:

dict您可以通过调用带有附加关键字参数的构造函数来一次性复制和编辑新构建的副本:

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

解决方案 11:

一开始这也让我很困惑,因为我有 C 语言背景。

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

但在 Python 中,变量的作用更像是指向对象的指针。因此,将一个变量赋值给另一个变量并不会进行复制,而只是让该变量名指向同一个对象。

解决方案 12:

dict1是引用底层字典对象的符号。将 分配dict1dict2只会分配相同的引用。通过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:

Python 中的每个变量(诸如dict1或之类的东西str__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多——编写需要它的代码通常是不好的做法(在我看来)。

解决方案 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:

dict2 = dict1不会复制字典。它只是为程序员提供了第二种方式 ( dict2) 来引用同一字典。

解决方案 16:

以下代码是关于 dicts 的,它遵循 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)

解决方案 17:

正如其他人所解释的那样,内置函数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, \n            "Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k)

    def __delitem__(self, k):
        raise AttributeError, \n            "Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k)

    def update(self, d2):
        raise AttributeError, \n            "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 - 左值修改的简洁语法。关键的观察是和str在Python 中表现为值(即使它们实际上是底层的不可变对象)。在观察这一点的同时,请注意或int并没有什么神奇的特殊之处。可以以相同的方式使用,我可以想到很多有意义的情况。str`intdictValueDict`

解决方案 18:

对于嵌套字典,请勿使用,dict(srcData) or srcData.copy() or {**srcData}因为如果您更改第二级或更多,它也会修改源字典

srcData = {
  'first': {
    'second': 'second Value'
  }
}
newData = dict(srcData) # srcData.copy() or {**srcData}
newData['first']['second'] = 'new Second Value'

print(srcData)
print(newData)

# it will print
# srcData: {'first': {'second': 'new Second Value'}}
# newData:{'first': {'second': 'new Second Value'}}

# but it should be
# srcData: {'first': {'second': 'second Value'}}
# newData:{'first': {'second': 'new Second Value'}}

deepcopy 的另一个选择是使用json像 Javascript 这样的技巧JSON.parse(JSON.stringify(obj))

import json

srcData = {'first': {'second': 'second Value'}}
newData = json.loads(json.dumps(srcData))
newData['first']['second'] = 'new Second Value'

print(srcData)
print(newData)

# srcData: {'first': {'second': 'second Value'}}
# newData: {'first': {'second': 'new Second Value'}}

解决方案 19:

当我尝试深度复制类的字典属性而不将其分配给变量时,我遇到了一种奇怪的行为

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

但如果你old = my_class.a这样做,new = copy.deepcopy(old)它就可以完美地工作,即修改new不会影响my_class.a

我不知道为什么会发生这种情况,但希望它能帮助节省一些时间!:)

解决方案 20:

这里的大多数答案都建议对字典进行浅拷贝,或者导入一个新模块。在大多数情况下,我发现了一种复制字典的方法,即每次可能时对单个项目使用 copy() 函数。

def deep_copy_dict( dict_in  ):
    """ Attempt deep copy of dict by iteratively adding key and related value, using copy() when possible """
    dict_out = dict()
    for k,v in dict_in.items():
        if hasattr(v,'copy'):
            dict_out[k] = v.copy()
        else:
            dict_out[k] = v
    return dict_out

尽管在项目没有 copy() 方法的特定情况下这可能会失败,但这应该可以涵盖大多数情况。

解决方案 21:

使用 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:

您可以直接使用:

dict2 = eval(repr(dict1))

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

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

解决方案 23:

另一种更简洁的方法是使用 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()。您将获得单独的字典对象。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用