澄清行为:collections.defaultdict vs dict.setdefault
clarify behaviour: collections.defaultdict vs dict.setdefault
dict
提供 .setdefault()
,这将允许您即时为缺失的键分配任何类型的值:
>>> d = dict()
>>> d.setdefault('missing_key', [])
[]
>>> d
{'missing_key': []}
然而,如果您使用 defaultdict
来完成相同的任务,那么每当您尝试访问或修改丢失的键时,都会按需生成默认值:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d['missing_key']
[]
>>> d
defaultdict(<class 'list'>, {'missing_key': []})
但是,以下使用 defaultdict
实现的代码片段引发 KeyError
而不是创建具有默认值的项目,{}
:
trie = collections.defaultdict(dict)
for word in words:
t = trie
for c in word:
t = t[c]
t["*"] = word
使用 .setdefault()
正常:
trie = {}
for word in words:
t = trie
for c in word:
t = t.setdefault(c, {})
t["*"] = word
访问前检查,工作正常:
trie = {}
for word in words:
t = trie
for c in word:
if c not in t:
t[c] = {}
t = t[c]
t["*"] = word
我在使用 collections.defaultdict()
时错过了什么?
注意
我正在尝试从单词列表中构建一个 Trie 结构。例如:
words = ["oath", "pea", "eat", "rain"]
trie = {'o': {'a': {'t': {'h': {'*': 'oath'}}}}, 'p': {'e': {'a': {'*': 'pea'}}}, 'e': {'a': {'t': {'*': 'eat'}}}, 'r': {'a': {'i': {'n': {'*': 'rain'}}}}}
在您的第一个示例中,当您执行 t = t[c] 时,t 变为常规空值 dict
(因为这就是您告诉 defaultdict
在 [= 的定义中生成的内容13=]).
让我们 运行 使用您的示例词 "oath"
完成循环:
1) t = trie, word = "oath"
2) c = "o"
3) t = t[c]
3.1) evaluation of t[c] # "o" is not in trie, so trie generates an empty dict at key "o" and returns it to you
3.2) assignment to t -> t is now the empty dict. If you were to run (t is trie["o"]), it would evaluate to True after this line
4) c = "a"
5) t = t[c]
5.1) Evaluation of t[c] -> "a" is not in the dict t. This is a regular dict, raise KeyError.
遗憾的是,我这里()想不出使用defaultdict
的方法,因为Trie的任意嵌套。您需要将 trie 定义为 defaultdict,如果缺少键,它会生成一个默认字典 ,如果缺少键 ,它本身会生成一个默认字典,递归直到最大深度(这在原则上是未知的)。
IMO,实现这一点的最佳方法是使用 setdefault
,就像您在第二个示例中所做的那样。
非常好,事实上,我也相信 setdefault
是正确的做法。
然而,如果有人喜欢使用 defaultdict
,可以像这样轻松实现:
def ddnestedconst():
"""collections.defaultdict nested-ish constructor."""
return collections.defaultdict(ddnestedconst)
# ...
trie = collections.defaultdict(ddnestedconst)
# or, being the same:
#trie = ddnestedconst()
for word in words:
t = trie
for c in word:
t = t[c]
t["*"] = word
起初可能会觉得有点奇怪,但我发现它完全可读且语义准确。
达到这一点后,您可能还希望完全创建一个新的 class,受 defaultdict
启发,但包括您期望从中获得的所有语义和特定行为。
dict
提供 .setdefault()
,这将允许您即时为缺失的键分配任何类型的值:
>>> d = dict()
>>> d.setdefault('missing_key', [])
[]
>>> d
{'missing_key': []}
然而,如果您使用 defaultdict
来完成相同的任务,那么每当您尝试访问或修改丢失的键时,都会按需生成默认值:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d['missing_key']
[]
>>> d
defaultdict(<class 'list'>, {'missing_key': []})
但是,以下使用 defaultdict
实现的代码片段引发 KeyError
而不是创建具有默认值的项目,{}
:
trie = collections.defaultdict(dict)
for word in words:
t = trie
for c in word:
t = t[c]
t["*"] = word
使用 .setdefault()
正常:
trie = {}
for word in words:
t = trie
for c in word:
t = t.setdefault(c, {})
t["*"] = word
访问前检查,工作正常:
trie = {}
for word in words:
t = trie
for c in word:
if c not in t:
t[c] = {}
t = t[c]
t["*"] = word
我在使用 collections.defaultdict()
时错过了什么?
注意 我正在尝试从单词列表中构建一个 Trie 结构。例如:
words = ["oath", "pea", "eat", "rain"]
trie = {'o': {'a': {'t': {'h': {'*': 'oath'}}}}, 'p': {'e': {'a': {'*': 'pea'}}}, 'e': {'a': {'t': {'*': 'eat'}}}, 'r': {'a': {'i': {'n': {'*': 'rain'}}}}}
在您的第一个示例中,当您执行 t = t[c] 时,t 变为常规空值 dict
(因为这就是您告诉 defaultdict
在 [= 的定义中生成的内容13=]).
让我们 运行 使用您的示例词 "oath"
完成循环:
1) t = trie, word = "oath"
2) c = "o"
3) t = t[c]
3.1) evaluation of t[c] # "o" is not in trie, so trie generates an empty dict at key "o" and returns it to you
3.2) assignment to t -> t is now the empty dict. If you were to run (t is trie["o"]), it would evaluate to True after this line
4) c = "a"
5) t = t[c]
5.1) Evaluation of t[c] -> "a" is not in the dict t. This is a regular dict, raise KeyError.
遗憾的是,我这里(defaultdict
的方法,因为Trie的任意嵌套。您需要将 trie 定义为 defaultdict,如果缺少键,它会生成一个默认字典 ,如果缺少键 ,它本身会生成一个默认字典,递归直到最大深度(这在原则上是未知的)。
IMO,实现这一点的最佳方法是使用 setdefault
,就像您在第二个示例中所做的那样。
setdefault
是正确的做法。
然而,如果有人喜欢使用 defaultdict
,可以像这样轻松实现:
def ddnestedconst():
"""collections.defaultdict nested-ish constructor."""
return collections.defaultdict(ddnestedconst)
# ...
trie = collections.defaultdict(ddnestedconst)
# or, being the same:
#trie = ddnestedconst()
for word in words:
t = trie
for c in word:
t = t[c]
t["*"] = word
起初可能会觉得有点奇怪,但我发现它完全可读且语义准确。
达到这一点后,您可能还希望完全创建一个新的 class,受 defaultdict
启发,但包括您期望从中获得的所有语义和特定行为。