Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么collections.Counter构建频率图所花费的时间少于for循环创建简单dict所花费的时间?_Python_Python 3.x_Python Collections - Fatal编程技术网

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
可能是现存最糟糕的变量名:无意义且难以读取。