Python count()的奇怪执行时间
守则:Python count()的奇怪执行时间,python,python-3.x,list,performance,python-internals,Python,Python 3.x,List,Performance,Python Internals,守则: from timeit import Timer print(min(Timer('y=x.count(1)',setup='x=[1] * 1000').repeat(number=1000000))) print(min(Timer('y=x.count(0)',setup='x=[1] * 1000').repeat(number=1000000))) 我的机器上的结果: 0.7033228789223358 10.16116041096393 有人能解释为什么第一个病例比第
from timeit import Timer
print(min(Timer('y=x.count(1)',setup='x=[1] * 1000').repeat(number=1000000)))
print(min(Timer('y=x.count(0)',setup='x=[1] * 1000').repeat(number=1000000)))
我的机器上的结果:
0.7033228789223358
10.16116041096393
有人能解释为什么第一个病例比第二个病例快得多吗?我希望这两次都是相似的。这是因为您构建列表对象的方式:
x = [1] * 1000
这将创建一个仅包含一个对象的列表,引用次数为1000次;列表乘法不会创建值的副本。为了理解为什么这很重要,我们需要看看Python列表是如何计算的
list.count()
循环如下所示,是实现的一个快速Python翻译:
这很简单,对吧?然而,情况并非如此;实际代码使用的是PyObject\u richcomarebool()
,它是。这真的是:
if elem is value or elem == value:
当所有列表元素都是同一个对象时,身份测试(一个简单的指针相等测试)要快得多:
您可以使用任意随机值复制该值:
>>> from timeit import Timer
min(Timer('y=x.count(v)',setup='import random; v = random.randint(1000, 10000000); x=[v] * 1000').repeat(number=100000))
0.2716284029884264
>>> min(Timer('y=x.count(w)',setup='import random; v = random.randint(1000, 10000000); x=[v] * 1000; w = v + 1').repeat(number=100000))
1.0827720829984173
正如这些数字所示,在测试值相等性之前进行简单的指针比较是很有意义的。这正是Python实现实习生某些经常重用的值的原因,比如小整数(),或者也是有效Python标识符的字符串值
如果这里没有使用一个小整数作为
x.count()
的参数,它就不会工作;因为1
interned,x.count(1)
使用的对象也是列表的成员x[0]是1
是真的。我的Python 3.6.9在这里不同意-512是512
返回真的,1e23是1e23
也是如此,“a”是“a”
(但奇怪的是(1,)是(1,)
返回假的)@Błotosmętek:单个表达式值常量被插入。@Błotosmętek:单独的作用域不能重复使用常量,所以v=(lambda:512)();v为512
为假
。查看导入dis;dis.dis('512是512')
查看为什么它总是返回True
。Python可以在很多领域重复使用同一对象,而实现也很乐意这样做。@Błotosmętek:另外,至少在3.7和3.8中,(1,)是(1,)
也返回true,因为(1,)
只是与编译的字节码一起存储的另一个常量值。在3.6和更老版本中,这是错误的,因为在3.7中,窥视孔不断折叠的责任被转移到AST Optimizer,后者更聪明。
>>> import random
>>> v = random.randint(1000, 100000000)
>>> x = [v] * 1000
>>> all(value is v for value in x)
True
>>> from timeit import Timer
min(Timer('y=x.count(v)',setup='import random; v = random.randint(1000, 10000000); x=[v] * 1000').repeat(number=100000))
0.2716284029884264
>>> min(Timer('y=x.count(w)',setup='import random; v = random.randint(1000, 10000000); x=[v] * 1000; w = v + 1').repeat(number=100000))
1.0827720829984173