Python 如何使用 defaultdict 创建一个 dict of list 的字典

Python how to create a dict of dict of list with defaultdict

如何使用 defaultdict 创建列表的字典?我收到以下错误。

>>> from collections import defaultdict
>>> a=defaultdict()
>>> a["testkey"]=None
>>> a
defaultdict(None, {'testkey': None})
>>> a["testkey"]["list"]=[]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object does not support item assignment

您可能需要这样做。

>>> from collections import defaultdict
>>> a=defaultdict()
>>> a["testkey"]=None
>>> a["testkey"]=defaultdict(list)
>>> a["testkey"]["list"]=["a","b","c"]
>>> a
defaultdict(None, {'testkey': defaultdict(<type 'list'>, {'list': ['a', 'b', 'c']})})

有点棘手。你创建一个 defaultdicts 的 defaultdict,像这样:

defaultdict(lambda: defaultdict(list))

比使用 lambda 稍微快一点:

defaultdict(defaultdict(list).copy)

这与 具有相同的可观察行为,但避免了 lambda 支持在 C 中实现的(在 CPython 中)绑定内置方法,这意味着默认值生成不必执行任何 Python 字节代码或查找任何名称,并且运行速度会稍快一些。在 CPython 3.5 的微基准测试中,看起来当访问时密钥不存在时支付的成本比使用其他等效方法 lambda.[=36= 低 5-10% ]

真的,我喜欢它的原因是因为我讨厌 lambda 因为人们过度使用它是一个坏主意(例如 map/filterlambda总是比等效的 listcomp/genexpr 更冗长和更慢,但人们总是无缘无故地这样做),即使在这种情况下它并不重要。


更新: 从 3.8 开始,这种性能改进消失了,lambda 更快(在 3.8 上使用 lambda 减少了约 3% 的运行时间, ~7% on 3.9), 对于 ipython 的简单微基准测试。如果您想重现我的测试,我测试了:

>>> from collections import defaultdict
>>> %%timeit dd = defaultdict(lambda: defaultdict(list)); o = object
... dd[o()]

>>> %%timeit dd = defaultdict(defaultdict(list).copy); o = object
... dd[o()]

其中缓存 o = object 最大限度地减少了查找费用,并允许我们在不做任何其他工作的情况下制作非常便宜、有保证的唯一密钥(强制 list 的自动激活)。

3.8 中的性能改进可能主要是由于 per opcode cache for the LOAD_GLOBAL instruction 的引入,减少了在 lambda 中查找 defaultdictlist 的成本完整的 dict 查找(在 list 的情况下是两个,在内置函数中)快速检查 dict 上的版本标签,然后从缓存中廉价加载,减少成本降低约 40%。 3.9 的改进可能(对此不确定)与 CPython 的内部结构有关,以牺牲非向量调用代码路径(defaultdict(list).copy 路径使用更多,相对而言),甚至在这些改进之前,defaultdict(list).copy 有一些 lambda 所缺乏的低效率,为改进它提供了一些余地。