用 dis 模块分析为>什么 Python 2 中 while 1 比 while True 好
/ / 阅读数:1563摘要 :使用 Python 2 编程的时候,经常会看到有人说要用 while 1 来替代 while True,这是为什么呢?本文将带你使用 dis 内置模块来分析为什么是这样。
Python 代码是先被编译为 Python 字节码(Byte Code,文件后缀为 .pyc)后,再由 Python 虚拟机来执行 Python 字节码。
一般来说一个 Python 语句会对应若干字节码指令,Python 的字节码是一种类似汇编指令的中间语言, 但是一个字节码指令并不是对应一个机器指令(二进制指令),而是对应一段 C 代码(可以查看 cevel.c ), 而不同的指令的性能不同,所以一般不能单单通过指令数量来判断代码的性能。
Python 提供了 dis (Disassembler for Python bytecode,反汇编 Python 代码为字节码) 内置模块来分析和评估 Python 的性能和质量。
while 1 真的比 while True 好吗
我们通过以下代码进行演示:
$ python2 >>> def f1(): ... while 1: ... pass ... >>> def f2(): ... while True: ... pass ... >>> import dis >>> dis.dis(f1) 2 0 SETUP_LOOP 4 (to 7) 3 >> 3 JUMP_ABSOLUTE 3 6 POP_BLOCK >> 7 LOAD_CONST 0 (None) 10 RETURN_VALUE >>> dis.dis(f2) 2 0 SETUP_LOOP 10 (to 13) >> 3 LOAD_GLOBAL 0 (True) 6 POP_JUMP_IF_FALSE 12 3 9 JUMP_ABSOLUTE 3 >> 12 POP_BLOCK >> 13 LOAD_CONST 0 (None) 16 RETURN_VALUE |
参考官方文档说明 ,输出分为以下几列:
- 第一列:行号,用于每一行的第一条指令
- 第二列:当前执行的指令,用 --> 表示;
- 第三列:一个带标签(Labelled)的指令,用 >> 表示;
- 第四列:指令的地址;
- 第五列:操作代码名;
- 第六列:操作参数;
- 第七列:用括号括起来的参数解释。
对比发现,while True
版本比while 1
版本对应的字节码多了以下两条:
>> 3 LOAD_GLOBAL 0 (True) 6 POP_JUMP_IF_FALSE 12 |
所以while True
性能上也会有一定损失,while 1
确实好一些。
使用 timeit 进行验证
我们使用 timeit 模块测试一下耗时情况:
>>> def f1(): ... n = 1000 ... while 1: ... n -= 1 ... if n < 0: ... break ... >>> def f2(): ... n = 1000 ... while True: ... n -= 1 ... if n < 0: ... break ... >>> import timeit >>> timeit.timeit('f1()', number=100000, setup='from __main__ import f1, f2') 2.807568073272705 >>> timeit.timeit('f2()', number=100000, setup='from __main__ import f1, f2') 4.125874996185303 |
果然,对于上面简单的测试代码,while 1
版本确实快不少。
为什么 while True 生成更多字节码
原来在 Python 2 中,由于历史原因,True
和False
被实现为内置变量,而不是 Python 关键字,所以在使用的时候需要执行LOAD_GLOBAL
操作。
$ python2 >>> True = False # 可以对 True 内置变量进行覆盖 >>> True is False True |
但是在 Python 3 中,它们都已经变成关键字:
$ python3 >>> True = False File "<stdin>", line 1 SyntaxError: can't assign to keyword |
所以 Python 3 以后,while 1
和while True
编成的字节码就是一样的了:
$ python3 >>> def f1(): ... while 1: ... pass ... >>> def f2(): ... while True: ... pass ... >>> import dis >>> dis.dis(f1) 2 0 SETUP_LOOP 4 (to 6) 3 >> 2 JUMP_ABSOLUTE 2 4 POP_BLOCK >> 6 LOAD_CONST 0 (None) 8 RETURN_VALUE >>> dis.dis(f2) 2 0 SETUP_LOOP 4 (to 6) 3 >> 2 JUMP_ABSOLUTE 2 4 POP_BLOCK >> 6 LOAD_CONST 0 (None) 8 RETURN_VALUE |
结论
我们通过使用 dis 模块分析了在 Python 2 中为什么while 1
比while True
快,也发现在 Python 3 中,由于True
变成关键字,所以两者性能都一样。
其实不管是 Python 2 还是 Python 3,除非是性能要求非常高的关键代码,否则都用while True
就好了,毕竟使用 Python 的目的之一,就是要让代码有更好的可读性。
扩展
- 读者可以使用本分介绍的 dis 模块,分析一下为什么
if not x
比if x is None
快。 - Python
boolean
类型继承自int
,所以isinstance (True, int)
将返回True
,True == 1
也返回True
。