Python 为什么collections.Counter构建频率图所花费的时间少于for循环创建简单dict所花费的时间?
据我所知,由于Python 为什么collections.Counter构建频率图所花费的时间少于for循环创建简单dict所花费的时间?,python,python-3.x,python-collections,Python,Python 3.x,Python Collections,据我所知,由于count()。因为,计数器和dict构造都在列表上迭代一次 dict构造和计数器时间结果不应该相似吗 我使用代码作为获取时间值的参考 导入时间信息 如果名称=“\uuuuu main\uuuuuuuu”: 代码_block=“”seen={} 对于l中的i: 如果看到,获取(i): 见[i]+=1 其他: 见[i]=1 """ setup=“”随机导入 导入字符串 从收款进口柜台 n=1000 l=[random.choice(string.ascii_字母)表示范围(n)内的
count()。因为,计数器和dict构造都在列表上迭代一次
dict构造和计数器时间结果不应该相似吗
我使用代码作为获取时间值的参考
导入时间信息
如果名称=“\uuuuu main\uuuuuuuu”:
代码_block=“”seen={}
对于l中的i:
如果看到,获取(i):
见[i]+=1
其他:
见[i]=1
"""
setup=“”随机导入
导入字符串
从收款进口柜台
n=1000
l=[random.choice(string.ascii_字母)表示范围(n)内的x]
"""
t1=timeit.Timer(
stmt=“计数器(l)”,
设置=设置,
)
t2=timeit.Timer(
stmt=“[x,l.计数(x)],用于集合(l)]中的x,
设置=设置,
)
t3=timeit.Timer(
stmt=代码块,
设置=设置,
)
打印(“计数器():”,t1.repeat(repeat=3,number=10000))
打印(“count():”,t2.重复(重复=3,数字=10000))
打印(“见{}:”,t3.重复(重复=3,数字=10000))
输出:
Counter(): [0.48620019999999997, 0.49807440000000014, 0.3896322000000001]
count(): [9.7432961, 9.620701299999999, 9.674791500000001]
seen{}: [1.4734368999999994, 1.462895500000002, 1.4655799000000016]
运行1:
运行2:
Counter(): [0.322483783, 0.32464020800000004, 0.33498838900000005]
count(): [6.3235339029999995, 6.48233445, 6.543396192000001]
seen{}: [1.1192663550000006, 1.1072084830000009, 1.1155270229999985]
我更正了您的代码,以便在所有三个部分上执行可比较的操作:导入相同的包(以平衡开销),然后搜索整个字符列表,而不是将其减少为唯一的字符串——这是“SEED”代码中的主要错误:您只计算了每个字符中的一个
import timeit
if __name__ == '__main__':
code_block = '''seen = {}
for i in char:
if seen.get(i):
seen[i] += 1
else:
seen[i] = 1
'''
common = 'import random;import string;from collections import Counter;n=1000;' + \
'char=[random.choice(string.ascii_letters) for x in range(n)]'
t1 = timeit.Timer(stmt='Counter(char)',
setup=common)
t2 = timeit.Timer(stmt='[[x,char.count(x)] for x in set(char)]',
setup=common)
t3 = timeit.Timer(stmt=code_block,
setup=common)
print("Counter(): ", t1.repeat(repeat=3, number=10000))
print("count(): ", t2.repeat(repeat=3, number=10000))
print("seen{}: ", t3.repeat(repeat=3, number=10000))
输出:
Counter(): [0.48620019999999997, 0.49807440000000014, 0.3896322000000001]
count(): [9.7432961, 9.620701299999999, 9.674791500000001]
seen{}: [1.4734368999999994, 1.462895500000002, 1.4655799000000016]
正如预期的那样,计数器是最快的。TL;DR
Counter.\uuuu init\uuuuu
正在使用C循环(至少在CPython中)来计算iterable的元素,请参阅
计数器
是用Python实现的(主要见下文),这意味着它的代码可以很容易地被检查、调试甚至修改
计数器。CPython 3.8中的\uuuuu init\uuuuu
(不包括docstring):
和计数器。更新
(不包括不相关的路径):
和\u计数\u元素
:
def _count_elements(mapping, iterable):
mapping_get = mapping.get
for elem in iterable:
mapping[elem] = mapping_get(elem, 0) + 1
try: # Load C helper function if available
from _collections import _count_elements
except ImportError:
pass
然而,在\u count\u元素的实现下方有一段非常重要的代码+注释
:
def _count_elements(mapping, iterable):
mapping_get = mapping.get
for elem in iterable:
mapping[elem] = mapping_get(elem, 0) + 1
try: # Load C helper function if available
from _collections import _count_elements
except ImportError:
pass
换句话说,Counter
使用一个C循环来计算元素,这比您正在使用的Python循环本身要快
我们可以做一个小实验。注释掉导入C函数的代码:
# try: # Load C helper function if available
# from _collections import _count_elements
# except ImportError:
# pass
执行代码:
Counter(): [1.8369901, 1.8359803000000001, 1.940804]
seen{}: [1.2392313000000001, 1.2483893999999998, 1.3157528000000003]
现在,计数器实际上比普通的dict慢。请注意,timeit
也可以使用常规功能。没有必要把问题的相关部分塞进这样难以辨认的字串里。@Mistermiagi将在将来处理。我使用了这个答案中的代码片段检查您的代码。首先,在for
循环中,从输入列表l
中设置一个set
(有效减小大小)。您的dict
解决方案将不会产生与计数器
解决方案相同的结果。此外,我还将使用相同的输入列表运行3个备选方案。@buran My bad,我更改了代码以反映新时代。那么,现在您将问题颠倒过来了吗?当您正在更正代码时:(如果看到)。get(I):
应该是如果我看到:
中的是我自己的偏好,但不是OP的。我所做的唯一的非功能性修正是用char
替换l
,因为l
可能是现存最糟糕的变量名:无意义且难以读取。