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
/filter
和 lambda
总是比等效的 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
中查找 defaultdict
和 list
的成本完整的 dict
查找(在 list
的情况下是两个,在内置函数中)快速检查 dict
上的版本标签,然后从缓存中廉价加载,减少成本降低约 40%。 3.9 的改进可能(对此不确定)与 CPython 的内部结构有关,以牺牲非向量调用代码路径(defaultdict(list).copy
路径使用更多,相对而言),甚至在这些改进之前,defaultdict(list).copy
有一些 lambda
所缺乏的低效率,为改进它提供了一些余地。
如何使用 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)
这与 真的,我喜欢它的原因是因为我讨厌 更新: 从 3.8 开始,这种性能改进消失了, 其中缓存 3.8 中的性能改进可能主要是由于 per opcode cache for the lambda
支持在 C 中实现的(在 CPython 中)绑定内置方法,这意味着默认值生成不必执行任何 Python 字节代码或查找任何名称,并且运行速度会稍快一些。在 CPython 3.5 的微基准测试中,看起来当访问时密钥不存在时支付的成本比使用其他等效方法 lambda
.[=36= 低 5-10% ]
lambda
因为人们过度使用它是一个坏主意(例如 map
/filter
和 lambda
总是比等效的 listcomp/genexpr 更冗长和更慢,但人们总是无缘无故地这样做),即使在这种情况下它并不重要。
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
的自动激活)。LOAD_GLOBAL
instruction 的引入,减少了在 lambda
中查找 defaultdict
和 list
的成本完整的 dict
查找(在 list
的情况下是两个,在内置函数中)快速检查 dict
上的版本标签,然后从缓存中廉价加载,减少成本降低约 40%。 3.9 的改进可能(对此不确定)与 CPython 的内部结构有关,以牺牲非向量调用代码路径(defaultdict(list).copy
路径使用更多,相对而言),甚至在这些改进之前,defaultdict(list).copy
有一些 lambda
所缺乏的低效率,为改进它提供了一些余地。