while (1) vs. while(True) -- Why is there a difference (in python 2 bytecode)?

Intrigued by this question about infinite loops in perl: while (1) Vs. for (;;) Is there a speed difference?, I decided to run a similar comparison in python. I expected that the compiler would generate the same byte code for while(True): pass and while(1): pass, but this is actually not the case in python2.7.

The following script:

import dis


def while_one():
while 1:
pass


def while_true():
while True:
pass


print("while 1")
print("----------------------------")
dis.dis(while_one)


print("while True")
print("----------------------------")
dis.dis(while_true)

produces the following results:

while 1
----------------------------
4           0 SETUP_LOOP               3 (to 6)


5     >>    3 JUMP_ABSOLUTE            3
>>    6 LOAD_CONST               0 (None)
9 RETURN_VALUE
while True
----------------------------
8           0 SETUP_LOOP              12 (to 15)
>>    3 LOAD_GLOBAL              0 (True)
6 JUMP_IF_FALSE            4 (to 13)
9 POP_TOP


9          10 JUMP_ABSOLUTE            3
>>   13 POP_TOP
14 POP_BLOCK
>>   15 LOAD_CONST               0 (None)
18 RETURN_VALUE

Using while True is noticeably more complicated. Why is this?

In other contexts, python acts as though True equals 1:

>>> True == 1
True


>>> True + True
2

Why does while distinguish the two?

I noticed that python3 does evaluate the statements using identical operations:

while 1
----------------------------
4           0 SETUP_LOOP               3 (to 6)


5     >>    3 JUMP_ABSOLUTE            3
>>    6 LOAD_CONST               0 (None)
9 RETURN_VALUE
while True
----------------------------
8           0 SETUP_LOOP               3 (to 6)


9     >>    3 JUMP_ABSOLUTE            3
>>    6 LOAD_CONST               0 (None)
9 RETURN_VALUE

Is there a change in python3 to the way booleans are evaluated?

100145 次浏览

在 Python 2.x 中,True不是关键字,而只是 bool类型中定义为1的 内建的全局常数。因此,解释器仍然必须加载 True的内容。换句话说,True是可重新分配的:

Python 2.7 (r27:82508, Jul  3 2010, 21:12:11)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 4
>>> True
4

在 Python 3. x it truly becomes a keyword中,一个真正的常量:

Python 3.1.2 (r312:79147, Jul 19 2010, 21:03:37)
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 4
File "<stdin>", line 1
SyntaxError: assignment to keyword

因此解释器可以用无限循环代替 while True:循环。

称之为“无限循环”并不完全正确

因此解释器可以用无限循环替换 while True: 循环。

because one can still break out of such a while True: loop. But such a loop's else clause would never be accessed in Python 3.

Python3对 True值查找的简化使其运行速度与 Python2中的 while 1一样快。

性能比较

演示一个稍微有些重要的 while 循环的时间差:

设置

def while1():
x = 0
while 1:
x += 1
if x == 10:
break
            

def whileTrue():
x = 0
while True:
x += 1
if x == 10:
break

巨蟒2

>>> import timeit
>>> min(timeit.repeat(while1))
0.49712109565734863
>>> min(timeit.repeat(whileTrue))
0.756627082824707

Python 3

>>> import timeit
>>> min(timeit.repeat(while1))
0.6462970309949014
>>> min(timeit.repeat(whileTrue))
0.6450748789939098

解释

To explain the difference, in Python 2:

>>> import keyword
>>> 'True' in keyword.kwlist
False

but in Python 3:

>>> import keyword
>>> 'True' in keyword.kwlist
True
>>> True = 'true?'
File "<stdin>", line 1
SyntaxError: can't assign to keyword

由于 True是 Python3中的关键字,因此解释器不必查找该值,以查看是否有人用其他值替换了它。但是,由于可以将 True赋给 Python2中的另一个值,因此解释器每次都必须查找它。

Python 2的结论

如果在 Python2中有一个紧凑的、长时间运行的循环,那么可能应该使用 while 1:而不是 while True:

Conclusion for Python 3

如果没有突破循环的条件,则使用 while True:

这是一个7年前的问题,已经有了一个很好的答案,但是这个问题中的一个错误概念,没有在任何一个答案中解决,使得它可能对其他一些标记为重复的问题造成混淆。

在其他情况下,python 的行为就好像 True 等于1:

>>> True == 1
True


>>> True + True
2

为什么同时区分两者?

事实上,while在这里没有任何不同。它以与 +示例完全相同的方式区分 1True


这是2.7:

>>> dis.dis('True == 1')
1           0 LOAD_GLOBAL              0 (True)
3 LOAD_CONST               1 (1)
6 COMPARE_OP               2 (==)
9 RETURN_VALUE


>>> dis.dis('True == 1')
1           0 LOAD_GLOBAL              0 (True)
3 LOAD_GLOBAL              0 (True)
6 BINARY_ADD
9 RETURN_VALUE

现在比较一下:

>>> dis.dis('1 + 1')
1           0 LOAD_CONST               1 (2)
3 RETURN_VALUE

It's emitting a LOAD_GLOBAL (True) for each True, and there's nothing the optimizer can do with a global. So, while distinguishes 1 and True for the exact same reason that + does. (And == doesn't distinguish them because the optimizer doesn't optimize out comparisons.)


现在比较一下3.6:

>>> dis.dis('True == 1')
1           0 LOAD_CONST               0 (True)
2 LOAD_CONST               1 (1)
4 COMPARE_OP               2 (==)
6 RETURN_VALUE


>>> dis.dis('True + True')
1           0 LOAD_CONST               1 (2)
2 RETURN_VALUE

在这里,它为关键字发出 LOAD_CONST (True),优化器 can利用了这一点。因此,True + 1 doesn't区分,完全相同的原因 while True没有。(==仍然没有区分它们,因为优化器不会优化比较。)


同时,如果代码没有得到优化,那么解释器最终会在这三种情况下对 True1的处理完全相同。boolint的一个子类,从 int继承了它的大部分方法,而 True的内部整数值为1。所以,无论你是在做 while测试(__bool__在3.x,__nonzero__在2.x) ,比较(__eq__) ,还是算术(10) ,无论你使用 True还是 1,你都在调用相同的方法。