defaultdict与dict元素初始化比较
我正在尝试优化一个脚本,这个脚本的功能是查找每个给定单词的相似词。
每个独特的单词会被拆分成字母的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 个回答
当你访问一个 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
任何对一个键的访问都会生成对应的值:
>>> 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
。
唉,我发现了我代码里的错误。
因为我的输入数据中有很多相同的词组,我只为每个独特的测试词创建一次相似词的字典。
这个字典之后会被用来做其他事情,而这些字典的键会被多次测试。当然,这样可能会出现零值的元素,如果字典是collections.defaultdict
,而默认工厂没有设置为None
。
不过,我在每个主循环中都检查了零值元素,所以会找到在之前循环中创建的零值元素。
在把检查代码放到正确的位置后,结果如我所预期的那样——创建后没有零值元素。
我想对大家说声抱歉,因为我提问时的构造有问题和不完整,导致其他人很难找到错误。