我有一个程序,在我的模拟中每年必须复制一些属性的字典,并且必须更新一些键。但是,当我向一个函数发送一个字典时,在函数内部更改它并返回它,返回的字典保留作为对原始字典的引用。让我用下面的代码展示一个简单的例子。
def change(dict_in):
dict_in['value'] = 50
return dict_in
props = [{'value':12}]
props.append(change(props[-1]))
props
[{'value': 50}, {'value': 50}]
然而,正如上面所见,第一个字典中的属性“值”也发生了变化。
当我使用copy.deepcopy函数时,它按预期工作:
import copy
props = [{'value':12}]
props.append( change( copy.deepcopy(props[-1]) ) )
props
[{'value': 12}, {'value': 50}]
但这是让它发挥作用的唯一方法吗?
答案 0 :(得分:1)
dict.update()
接近def change(dict_in):
new_dict = {}
new_dict.update(dict_in)
new_dict['value'] = 50
return new_dict
props = [{'value':12}]
props.append(change(props[-1]))
props
[{'value': 50}, {'value': 50}]
d1.update(d2)
未返回结果,它会修改d1
以包含d2
值。这就是为什么我首先创建一个新的空dict
并将输入值复制到其中。
dict()
接近def change(dict_in):
new_dict = dict(dict_in)
new_dict['value'] = 50
return new_dict
props = [{'value':12}]
props.append(change(props[-1]))
props
[{'value': 50}, {'value': 50}]
与之前的版本类似,但使用dict
构造函数
dict
理解方法def change(dict_in):
new_dict = {k: v for k, v in dict_in.items()}
new_dict['value'] = 50
return new_dict
props = [{'value':12}]
props.append(change(props[-1]))
props
[{'value': 50}, {'value': 50}]
另一种使用dict
理解的复制方法。
**kwargs
接近def change(**kwargs):
kwargs['value'] = 50
return kwargs
props = [{'value':12}]
props.append(change(**props[-1]))
props
[{'value': 50}, {'value': 50}]
函数形式参数(在**
行)之前的def
表示法意味着未明确指定的关键字参数将存储为dict
。 kwargs
是此变量的通用名称。单个*
的位置参数类似于将它们存储在list
。
调用函数时的**
表示法意味着相反,将dict
值提取到关键字参数中。与*
和列表相同。
这样,我们会将原始props[-1]
中的dict
提取到一组关键字参数中,并使用dict
创建新的**kwargs
。我实际上喜欢这种方法,因为你让Python处理新的dict
创建,但你必须记住在调用change时使用**
。
答案 1 :(得分:0)
如果您在deepcopy
和update
中看到的问题是在每次模拟迭代后都会创建字典副本,则可以考虑使用不可变字典类型。不幸的是,python标准库不提供不可变的dicts。但是,数据结构在pyrsistent等库中可用。来自pmap的高级文档:
>>> from pyrsistent import m, pmap, v
# No mutation of maps once created, instead they are
# "evolved" leaving the original untouched
>>> m1 = m(a=1, b=2)
>>> m2 = m1.set('c', 3)
>>> m3 = m2.set('a', 5)
>>> m1
pmap({'a': 1, 'b': 2})
>>> m2
pmap({'a': 1, 'c': 3, 'b': 2})
>>> m3
pmap({'a': 5, 'c': 3, 'b': 2})
>>> m3['a']
5
答案 2 :(得分:0)
如果它适合您的程序,您可以将数据分为两部分:恒定基数保持不变(以及您现在从一个模拟轮次复制到下一个模拟轮次)以及其余部分,即更改或更新。您可以使用ChainMap加入这两个部分。它在Python 3上可用,但如果您使用的是Python 2,则可能需要向后移植它。
这是一个例子。
from collections import ChainMap
base = dict(a=1, b=2, c=3, d=4)
updates = [
dict(a=99),
dict(b=99),
dict(a=0, b=0, c=0),
]
for i, update in enumerate(updates, 1):
combined = ChainMap(update, base)
print("#{}: a={}, b={} c={} d={}".format(
i, combined['a'], combined['b'], combined['c'], combined['d']))
#1: a=99, b=2 c=3 d=4
#2: a=1, b=99 c=3 d=4
#3: a=0, b=0 c=0 d=4