阐明行为:collections.defaultdict与dict.setdefault

时间:2020-07-28 09:25:52

标签: python python-3.x dictionary 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()时我缺少什么?

NB 我正在尝试从单词列表中构建一个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'}}}}}

2 个答案:

答案 0 :(得分:3)

在第一个示例中,当您执行t = t [c]时,t变成常规的空dict(因为这就是您告诉defaultdict在{{1}的定义中生成的内容) }。

让我们用示例词trie遍历循环:

"oath"

不幸的是,由于Trie的任意嵌套,我无法想到在此使用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. but Marius could, see this answer)的方法。您需要将trie定义为defaultdict,在缺少键的情况下会生成默认字典,在缺少键的情况下本身会生成默认字典,递归直到最大深度(在原理,是未知的。)

IMO,实现此目标的最佳方法是使用defaultdict,就像您在第二个示例中所做的那样。

答案 1 :(得分:2)

GPhilo's answer很好,实际上,我也相信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

乍一看可能有些奇怪,但我发现它完全可读且语义准确。

在这一点上,您可能更喜欢根据defaultdict的启发创建一个新类,但包括您期望的所有语义和特定行为。