我正在尝试打印1到1000之间的数字。
print(*range(1,1001))
比打印列表print([*range(1,1001)])
花费更多的时间。
为什么打印列表比打印数字行要快得多?
答案 0 :(得分:2)
在这里您可能要问两件事。
由于诸如此类的事情,第二个会更快:
CALL_FUNCTION_EX
而不是通过更简单的CALL_FUNCTION
,实际上是经过优化的快速路径。)list.__repr__
内遍历列表元素,而不是使用通用的迭代器循环。print
在每个元素的__str__
之间执行的操作比list.__repr__
在每个元素的__repr__
之间执行的操作稍微复杂(有条件地打印局部变量) ,而不是始终使用固定的字符串)。这应该是几微秒的时间-足以测量 ,但不足以发出 notice 。
但是第二个也会也更快,因为它减少了I / O调用。这可能会淹没所有这些小的差异。而且,如果您的终端运行缓慢,例如Windows cmd
或IDLE伪终端,这很容易引起注意。
首先让我们尝试调用一个绝对不做任何事情的函数:
In [765]: def dummy(*args): pass
In [766]: %timeit dummy(*range(1, 1001))
19 µs ± 71.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [767]: %timeit dummy([*range(1, 1001)])
13 µs ± 609 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
因此,第二个仅由于参数传递而快了50%,但这仅是6us。
如果以有效的方式print
迭代其参数怎么办?
In [768]: def dummy(*args):
...: for _ in args: pass
In [769]: %timeit dummy(*range(1, 1001))
22.8 µs ± 1.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [770]: %timeit dummy([*range(1, 1001)])
13.1 µs ± 148 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
现在它越来越接近2:1,但仍然只有9us的差异。
当然,我在这里有点作弊,因为print
是C函数,但是无论哪种方式,*args
都变成一个元组,它们必须以某种方式循环。 >
为了更公平地比较,如果它也调用每个参数的__str__
怎么办?
In [776]: def dummy(*args):
...: for arg in args: str(arg)
In [776]: %timeit dummy(*range(1, 1001))
185 µs ± 1.67 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [777]: %timeit dummy([*range(1, 1001)])
86.3 µs ± 826 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
即使试图使事情对第一个更加公平,仍然约为2:1。
让我们实际调用print
,但打印到一个文件对象,该对象将丢弃其输入:
In [747]: class Nully:
...: def write(self, *args): pass
In [749]: null = Nully()
In [750]: %timeit print(*range(1, 1001), file=null)
390 µs ± 7.44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [751]: %timeit print([*range(1, 1001)], file=null)
88.4 µs ± 2.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
现在,第二个的速度大约是它的4倍,但是我们只聊了几分之一毫秒。
现在让我们尝试连接实际的I / O,但连接到空设备:
In [745]: %timeit with open(os.devnull, 'w') as null: print(*range(1, 1001), file=null)
436 µs ± 13.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [746]: %timeit with open(os.devnull, 'w') as null: print([*range(1, 1001)], file=null)
140 µs ± 1.74 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
它们的速度都降低了同样的小幅度。
现在,让我们尝试向每次写入花费较长时间(例如10毫秒)的文件进行写入:
In [767]: class Slowy:
...: def write(self, *args): time.sleep(0.01)
In [768]: null = Slowy()
In [770]: %timeit print(*range(1, 1001), file=null)
26.8 s ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [771]: %timeit print([*range(1, 1001)], file=null)
28.2 ms ± 39.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
最初的300us差异可能仍然存在,但是谁在乎呢?在这里重要的是由1000次写入而不是1次引起的3阶27秒差异。
当然,即使cmd.exe
和IDLE也不慢。但是它们非常慢。
所以,我的猜测是,最后一部分就是您要问的问题。
实际上,从后来添加的评论中可以找到
:这是人类明显的延迟。
timeit.timeit("print(*range(1,1001))",number=1) => 10 s
timeit.timeit("print([*range(1,1001)])",number=1) => 95 ms
顺便说一句,我正在使用Windows,并在Python IDLE上运行此代码。
所以我错了:IDLE 是差不多完全这么慢。 (哇!)
答案 1 :(得分:0)
您似乎是对的,这是我的计算机(Mac OSX)上的输出
import timeit
# 1
timeit.timeit('print(*range(1,1001))',number=10000)
7.32013652799651
# 2
timeit.timeit('print([*range(1,1001)])',number=10000)
3.6037830549757928
我要说的原因在于,通过使用第二种方法,您将向print
函数传递一个列表,而使用第一种方法,将print
函数与该列表一起调用项目作为单独的参数。
查看dis()
dis.dis('print(*range(1,10))')
0 LOAD_NAME 0 (print)
2 LOAD_NAME 1 (range)
4 LOAD_CONST 0 (1)
6 LOAD_CONST 1 (10)
8 CALL_FUNCTION 2
10 CALL_FUNCTION_EX 0
12 RETURN_VALUE
dis.dis('print([*range(1,10)])')
0 LOAD_NAME 0 (print)
2 LOAD_NAME 1 (range)
4 LOAD_CONST 0 (1)
6 LOAD_CONST 1 (10)
8 CALL_FUNCTION 2
10 BUILD_LIST_UNPACK 1
12 CALL_FUNCTION 1
14 RETURN_VALUE
在第二种情况下,可能会看到在调用打印函数之前,列表的各项已连接(BUILD_LIST_UNPACK
)。
答案 2 :(得分:-1)
打印数字将一次获得一个值 打印列表将一次添加整个列表。