对于这种特定情况,请使用 acollections.Counter()
或collections.defaultdict()
object 代替:
import collections
dct = collections.defaultdict(int)
for c in string:
dict[c] += 1
或者
dct = collections.Counter(string)
两者都是标准dict
类型的子类。该Counter
类型添加了一些更有用的功能,例如将两个计数器相加或列出已计数的最常见实体。该类defaultdict
也可以被赋予其他默认类型;defaultdict(list)
例如,用于将事物收集到每个键的列表中。
当您想比较两种不同方法的性能时,您需要使用timeit
模块:
>>> import timeit
>>> def intest(dct, values):
... for c in values:
... if c in dct:
... dct[c]+=1
... else:
... dct[c]=1
...
>>> def get(dct, values):
... for c in values:
... dct[c] = dct.get(c, 0) + 1
...
>>> values = range(10) * 10
>>> timeit.timeit('test(dct, values)', 'from __main__ import values, intest as test; dct={}')
22.210275888442993
>>> timeit.timeit('test(dct, values)', 'from __main__ import values, get as test; dct={}')
27.442166090011597
这表明使用in
速度有点快。
但是,还有第三种选择可以考虑;捕捉KeyError
异常:
>>> def tryexcept(dct, values):
... for c in values:
... try:
... dct[c] += 1
... except KeyError:
... dct[c] = 1
...
>>> timeit.timeit('test(dct, values)', 'from __main__ import values, tryexcept as test; dct={}')
18.023509979248047
这恰好是最快的,因为 10 个案例中只有 1 个用于新密钥。
最后但并非最不重要的一点是,我提出了两种选择:
>>> def default(dct, values):
... for c in values:
... dct[c] += 1
...
>>> timeit.timeit('test(dct, values)', 'from __main__ import values, default as test; from collections import defaultdict; dct=defaultdict(int)')
15.277361154556274
>>> timeit.timeit('Counter(values)', 'from __main__ import values; from collections import Counter')
38.657804012298584
所以Counter()
类型是最慢的,但defaultdict
确实非常快。Counter()
虽然 s 做了更多的工作,而且额外的功能可以在其他地方带来易于开发和执行速度的好处。