返回输入字典保留引用

时间:2017-10-10 06:09:48

标签: python dictionary reference

我有一个程序,在我的模拟中每年必须复制一些属性的字典,并且必须更新一些键。但是,当我向一个函数发送一个字典时,在函数内部更改它并返回它,返回的字典保留作为对原始字典的引用。让我用下面的代码展示一个简单的例子。

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

但这是让它发挥作用的唯一方法吗?

3 个答案:

答案 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表示法意味着未明确指定的关键字参数将存储为dictkwargs是此变量的通用名称。单个*的位置参数类似于将它们存储在list

调用函数时的**表示法意味着相反,将dict值提取到关键字参数中。与*和列表相同。

这样,我们会将原始props[-1]中的dict提取到一组关键字参数中,并使用dict创建新的**kwargs。我实际上喜欢这种方法,因为你让Python处理新的dict创建,但你必须记住在调用change时使用**

答案 1 :(得分:0)

如果您在deepcopyupdate中看到的问题是在每次模拟迭代后都会创建字典副本,则可以考虑使用不可变字典类型。不幸的是,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