defaultdict与dict元素初始化比较

4 投票
3 回答
5117 浏览
提问于 2025-04-18 02:35

我正在尝试优化一个脚本,这个脚本的功能是查找每个给定单词的相似词。

每个独特的单词会被拆分成字母的n-gram(就是把单词分成几个字母的小块),然后对于每个n-gram,词典会返回一个包含相同字母n-gram的单词列表。接着,这个列表中的每个单词都会被添加到一个字典中,作为键(key),它的值(value)会加一。这样我就得到了一个包含相似词和对应频率分数的字典。

word_dict = {}
get = word_dict.get
for letter_n_gram in word:
    for entry in lexicon[n_gram]:
        word_dict[entry] = get(entry, 0) + 1

这个实现方式是可行的,但据说通过把dict换成collections.defaultdict,脚本的运行速度可以更快。

word_dd = defaultdict(int)
for letter_n_gram in word:
    for entry in lexicon[n_gram]:
        word_dd[entry] += 1

其他的代码没有做任何改变。

我原以为这两段代码(特别是加分数的部分)应该是完全一样的,也就是说,如果这个键已经存在,就把它的值加1;如果不存在,就创建这个键,并把值设为1。

然而,在运行新代码后,我发现有些键的值竟然是0,这让我觉得很不合逻辑。

我的逻辑或者对defaultdict功能的理解有问题吗?如果没有,那word_dd中的任何值怎么会被设为0呢?

补充:我也很确定脚本的其他部分没有影响这些结果,因为我在使用以下代码后,立刻测试了这个字典:

for item in word_dd.iteritems():
    if item[1] == 0:
        print "Found zero value element"
        break

3 个回答

6

当你访问一个 defaultdict 中的键时,如果这个键不存在,它会自动创建一个。因为我们设置了 int 作为默认工厂函数,所以它会创建这个键,并给它一个默认值0。

from collections import defaultdict
d = defaultdict(int)
print d["a"]
# 0
print d
# defaultdict(<type 'int'>, {'a': 0})

所以,在访问这个键之前,你应该先确认它在 defaultdict 实例中是否存在,像这样做

print "a" in d
# False
6

任何对一个键的访问都会生成对应的值:

>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> d['foo']
0

可以使用包含性来测试这个键是否存在:

>>> 'bar' in d
False
>>> 'foo' in d
True

因为你在计算n-gram(n元组),你可能还想看看collections.Counter()这个工具:

from collections import Counter

word_counter = Counter()
for letter_n_gram in word:
    word_counter.update(lexicon[n_gram])

在这里,Counter.update()会更新所有由lexicon[n_gram]表达式返回的条目的计数。

就像defaultdict(int)一样,Counter()对象会自动生成值,默认是整数0

0

唉,我发现了我代码里的错误。

因为我的输入数据中有很多相同的词组,我只为每个独特的测试词创建一次相似词的字典。

这个字典之后会被用来做其他事情,而这些字典的键会被多次测试。当然,这样可能会出现零值的元素,如果字典是collections.defaultdict,而默认工厂没有设置为None

不过,我在每个主循环中都检查了零值元素,所以会找到在之前循环中创建的零值元素。

在把检查代码放到正确的位置后,结果如我所预期的那样——创建后没有零值元素。

我想对大家说声抱歉,因为我提问时的构造有问题和不完整,导致其他人很难找到错误。

撰写回答