附加到defaultdict中的键会产生意外结果

时间:2018-03-07 19:30:08

标签: python python-3.x defaultdict

from collections import defaultdict
phn_dictionary = {"actual": [], "predicted": []}
phn_dict = defaultdict(lambda: phn_dictionary)
phn_dict["qwe"]["actual"].extend([123,456])

phn_dict
>>>defaultdict(<function __main__.<lambda>>,
        {'qwe': {'actual': [123, 456], 'predicted': []}})

phn_dict["asd"]["actual"].extend([123,456])
phn_dict
>>>defaultdict(<function __main__.<lambda>>,
        {'asd': {'actual': [123, 456, 123, 456], 'predicted': []},
         'qwe': {'actual': [123, 456, 123, 456], 'predicted': []}})

我正在运行Python 3.6.4 64位。我需要使用一个defaultdict来生成phn_dictionary作为其默认值,如上面的代码所示。 我事先并不知道&#34; asd&#34;和&#34; qwe&#34;我将访问。可以看出,在我延伸到&#34; asd&#34; &#34;实际&#34; asd和qwe的关键是扩展。这是一个错误还是我做错了什么?

3 个答案:

答案 0 :(得分:3)

问题是lambda: phn_dictionary是一个返回phn_dictionary的函数 - 完全相同的字典对象 - 每次调用它时。因此,您最终得到的字典与一堆键的值相同。每次通过一个键追加时,所有其他键都可以看到。

你想要的不是这本词典,而是一本以该词典副本开头的新词典。正如Brendan Abel在评论中指出的那样,你可能想要一个副本 - 不只是一个新的dict,而是一个带有新列表的新dict:

phn_dict = defaultdict(lambda: copy.deepcopy(phn_dictionary))

或者,这可能更清楚(依赖于原始列表应该始终为空的事实):

phn_dict = defaultdict(lambda: {key: [] for key in phn_dictionary})

或者,如果除了这里你不需要phn_dictionary,只需使用Brendan的答案并在函数中从头开始创建dict:

phn_dict = defaultdict(lambda: {"actual": [], "predicted": []})

如果这是一个精简样本,并且真正的dict要大得多,或者变量等等,显然最后一个版本将不起作用,但如果这是真正的代码,那么它是最简单的。

还有其他方法可以解决这个问题,其中一些方法可能更清晰,但这是最符合内联lambda的方法,它似乎与你的思维方式相符。

答案 1 :(得分:1)

这是因为他们都代表同一个字典。如果您将工厂定义为返回字典文字,则可以解决问题

phn_dict = defaultdict(lambda: {"actual": [], "predicted": []})

这是因为每个时间调用默认的工厂lambda,它会返回一个新的字典,而不是一遍又一遍地返回相同的字典。

或者,您可以使用copy.deepcopy

phn_dict = defaultdict(lambda: copy.deepcopy(phn_dictionary))

这将复制定义的字典所有内部值。

答案 2 :(得分:0)

其他答案指出重用内部列表的引用。

除非确实想要提出KeyError如果对象使用了错误的密钥,否则你可以使用默认的listdict:

from collections import defaultdict
phn_dict = defaultdict(lambda: defaultdict(list))
phn_dict["qwe"]["actual"].extend([123,456])
phn_dict["qwe"]["predicted"].extend([768,333])
print(dict(phn_dict)) # clearer repr

结果:

{'qwe': defaultdict(<class 'list'>, {'actual': [123, 456], 'predicted': [768, 333]})}