如何将列表作为字典的默认值?

45

我有一个类似这样的Python代码:

if key in dict:
  dict[key].append(some_value)
else:
  dict[key] = [some_value]

但我想应该有某种方法可以避开这个 "if" 语句。我尝试过:

dict.setdefault(key, [])
dict[key].append(some_value)

dict[key] = dict.get(key, []).append(some_value)

但两者都抱怨“TypeError:unhashable type:'list'”。有什么建议吗?


看起来你可能在列表键方面遇到了麻烦,这与整个默认值的问题无关。或者,在实际代码中,你可能不小心交换了一些参数,而在发布到 SO 上时没有这样做。 - user2357112
1
你的异常与你在这里发布的代码无关。这表明key是一个列表对象,而不是可哈希的,因此不能用作字典键。 - Martijn Pieters
2
除了Martijn的设置默认值的答案之外,您还遇到了使用Python类名作为变量名的问题。当您说dict.setdefault(key, [])时,实际上是在调用'dict'类对象上未绑定的setdefault方法。它将'key'视为自身指针,并尝试使用'[]'作为索引。只需创建自己的变量mydict = dict(),您就可以进一步了。 - tdelaney
1
@tdelaney:从帖子的其余部分可以推断出,OP在某个时候执行了dict = {}。这是一个坏主意,因为它掩盖了内置函数。如果dict仍然是内置函数,则异常情况会有所不同:TypeError: descriptor 'setdefault' requires a 'dict' object but received a 'str'(对于key中的str值)。 - Martijn Pieters
哦,哇。我犯了一个大错误。我没有意识到我通过写dict = {}掩盖了一个内置函数。我的错。谢谢! - Fysx
1个回答

105

最佳方法是使用带有list默认值的collections.defaultdict

from collections import defaultdict
dct = defaultdict(list)

那么只需使用:

dct[key].append(some_value)

如果键还没有在映射中,字典将为您创建一个新列表。 collections.defaultdictdict 的子类,除此之外像普通的 dict 对象一样运作。

使用标准的 dict 时,dict.setdefault() 正确地为您设置了默认值 dct[key],因此该版本应该可以正常运行。 您可以在此调用中链接 .append()

>>> dct = {}
>>> dct.setdefault('foo', []).append('bar')  # returns None!
>>> dct
{'foo': ['bar']}

然而,通过使用 dct[key] = dct.get(...).append(),你会 替换 dct[key] 的值为 .append() 的输出,其值为 None


15
这个解决方案将我的执行时间从2小时缩短到了15分钟。我的字典非常大,一旦它们达到3000万条目,由于检查键是否已存在,它们就会变得缓慢。值得注意的是,我之前使用了networkx图形库,但内存开销太大了。现在,使用默认字典,与之前需要180GB RAM加载的相同图形,现在只需要不到20GB的RAM。你救了一个人。 - Chilli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接