我正在阅读关于loops的Python维基,它说
列表推导也在2.0版本中添加到Python中。它们提供了一种语法上更紧凑,更有效的编写上述for循环的方法:
然而,我发现当我测试这个时,我会得到一些意想不到的结果。
In [22]: def while_loop(n):
...: i = 0
...: while i < n:
...: i+=1
...:
In [23]: def while_loop_2(n):
...: while n > 0:
...: n-=1
...:
In [24]: def for_loop(n):
...: for _ in range(n):
...: pass
...:
In [30]: %timeit(for_loop(1000000))
10 loops, best of 3: 23.9 ms per loop
In [31]: %timeit(while_loop(1000000))
10 loops, best of 3: 37.1 ms per loop
In [32]: %timeit(while_loop_2(1000000))
10 loops, best of 3: 38 ms per loop
In [33]: %timeit([1 for _ in range(1000000)])
10 loops, best of 3: 43.2 ms per loop
这引出了一些问题:
为什么for
循环比列表理解快得多? (它似乎快了近两倍)
为什么while_loop_2
比while_loop
慢?为什么递增与递减计数器的差异会产生速度上的差异?我的天真使我相信更少的代码行=更快 - 显然情况并非如此
修改
这是在Python 2.7中完成的。在3.6 while_loop_2
实际上比while_loop
更快。所以新问题:
while
循环的区别是什么?答案 0 :(得分:6)
作为序言,你应该知道你的“比较”应该被孤立地分析(而不是相互比较),因为
for
循环,并且据说,我会回答问题#1。#1,因为dis
循环迭代。列表推导迭代,和在内存中创建一个列表。当然,这有助于节省总时间。仅此一点就足以说服你,但如果不是,请查看反汇编的字节代码,看看每个人在做什么。您可以使用dis
模块执行此操作。我实际上使用%%timeit
i = 0; n = 100000
while i < n: i += 1
11.5 ms ± 65.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
来回答你的第三个问题。
#2,至于这个,我无法在python3.6上重现。
%%timeit
n = 100000
while n > 0: n -= 1
10.8 ms ± 380 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
n > 0
通常,基于递减的循环应该更快一些,因为与0(i < n
)的比较通常比对非零值(import dis
)的比较更快。但三角洲通常“认真,不要担心它”有点小。
要回答#3,我们需要挖掘一下。我们来看看字节码。
dis.dis(
'''n = 100000
while n > 0: n -= 1'''
)
1 0 LOAD_CONST 0 (100000)
2 STORE_NAME 0 (n)
2 4 SETUP_LOOP 20 (to 26)
>> 6 LOAD_NAME 0 (n)
8 LOAD_CONST 1 (0)
10 COMPARE_OP 4 (>)
12 POP_JUMP_IF_FALSE 24
14 LOAD_NAME 0 (n)
16 LOAD_CONST 2 (1)
18 INPLACE_SUBTRACT
20 STORE_NAME 0 (n)
22 JUMP_ABSOLUTE 6
>> 24 POP_BLOCK
>> 26 LOAD_CONST 3 (None)
28 RETURN_VALUE
python3.6
dis.dis( '''n = 100000 while n > 0: n -= 1''' ) 0 JUMP_FORWARD 15648 (to 15651) 3 SLICE+2 4 <49> 5 <48> 6 <48> 7 <48> 8 <48> 9 <48> 10 UNARY_POSITIVE 11 CONTINUE_LOOP 26984 14 IMPORT_NAME 8293 (8293) 17 SLICE+2 18 JUMP_FORWARD 15904 (to 15925) 21 SLICE+2 22 <48> 23 INPLACE_DIVIDE 24 SLICE+2 25 JUMP_FORWARD 11552 (to 11580) 28 DELETE_SUBSCR 29 SLICE+2 30 <49>
python2.7
{{1}}
请注意,生成的字节码存在巨大差异。不同之处在于。
答案 1 :(得分:1)
您忘记在链接中测试above loop
:
newlist = []
for word in oldlist:
newlist.append(word.upper())
使用list append迭代:
In [104]: %%timeit
...: alist = []
...: for i in range(10000):
...: alist.append(i)
...:
1.07 ms ± 10.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
等效列表理解
In [105]: timeit [i for i in range(10000)]
491 µs ± 20.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
这确实比列表追加循环更快。
从范围对象创建相同的列表:
In [106]: timeit list(range(10000))
265 µs ± 679 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
不做任何迭代:
In [107]: %%timeit
...: for i in range(10000):
...: pass
...:
273 µs ± 9.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
其他一些列表创作:
In [109]: timeit list(map(lambda i:i, range(10000)))
1.41 ms ± 3.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [110]: timeit list(i for i in range(10000))
784 µs ± 19.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)