为了账号安全,请及时绑定邮箱和手机立即绑定

count() 的奇怪执行次数

count() 的奇怪执行次数

开满天机 2022-07-05 17:51:45
编码:from timeit import Timerprint(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.703322878922335810.16116041096393谁能解释为什么第一种情况比第二种情况快得多?我预计这两次都会相似。
查看完整描述

1 回答

?
炎炎设计

TA贡献1808条经验 获得超4个赞

这是由于您构建列表对象的方式:


x = [1] * 1000    

这将创建一个只有一个对象的列表,引用了 1000 次;列表乘法不会创建值的副本。要理解为什么这很重要,我们需要看看 Python 列表是如何计算的。


list.count()可以像这样看到循环,用 C 编写的实现的快速 Python 翻译:


def count(self, value):

    count = 0

    for elem in self:

        if elem == value:

            count += 1

    return count

这很简单,对吧?但是,情况并非如此。实际代码使用PyObject_RichCompareBool(),它首先测试对象身份。是真的:


if elem is value or elem == value:

当所有列表元素都是同一个对象时,身份测试(简单的指针相等测试)要快得多:


>>> 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

正如这些数字所示,在测试值相等之前进行简单的指针比较是很有意义的。这正是 Python 实现实习生某些经常重用的值的原因,例如小整数(介于 -5 和 256 之间的整数),或者也是有效 Python 标识符的字符串值。


如果你没有在这里使用一个小整数作为x.count(); 的参数,它就不会起作用。这是因为 1internedx.count(1)使用的对象也是列表的成员;x[0] is 1是真的。


查看完整回答
反对 回复 2022-07-05
  • 1 回答
  • 0 关注
  • 110 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信