while(1)vs. while(True)—为什么会有区别(在python 2字节码中)?


115

这个问题引起了有关perl中无限循环的问题:while(1)Vs。for(;;)是否存在速度差异?,我决定在python中运行类似的比较。我期望编译器会为while(True): pass和生成相同的字节码while(1): pass,但是python2.7实际上不是这种情况。

以下脚本:

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)

产生以下结果:

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        

使用while True明显更复杂。为什么是这样?

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

>>> True == 1
True

>>> True + True
2

为什么要while区分两者?

我注意到python3确实使用相同的操作评估语句:

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         

python3中的布尔值评估方式是否有变化?


Answers:


124

在Python 2.x中,True它不是关键字,而只是一个在类型中定义为1 的内置全局常量bool。因此,解释器仍然必须加载的内容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中,它实际上变成了关键字和实常数:

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:循环替换为无限循环。


1
@MH:AFAIK,将关键字加入语言是很方便的。
S.Lott


这是否意味着while 1while TruePython 3中相同?
Stevoisiak

@ StevenM.Vascellaro是的。
kennytm

14

这不太正确

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

因为仍然可以打破循环。但是,确实如此的循环else子句永远不会在Python 3中访问。而且,简化值查找使其与while 1在Python 2中一样快运行,这也是事实。

性能比较

演示一个不平凡的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

Python 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

说明

为了解释差异,在Python 2中:

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

但在Python 3中:

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

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

Python 2总结

如果您在Python 2中有一个紧密而长期运行的循环,则可能应该使用while 1:而不是while True:

Python 3总结

while True:如果您没有打破循环的条件,请使用。


3

这是一个已有7年历史的问题,已经有了一个不错的答案,但是这个问题的误解(任何一个答案都没有解决)使它可能会混淆其他一些标记为重复的问题。

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

>>> True == 1
True

>>> True + True
2

为什么同时区分两者?

实际上,while这里根本没有做任何不同的事情。它区分1,并True以完全同样的方式,+例如做。


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

LOAD_GLOBAL (True)为每个发出一个,True优化器对全局没有任何作用。因此,while区分1True出于完全相同的原因+。(并且==不区分它们,因为优化器不会优化比较。)


现在比较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)为关键字发出一个,优化器可以利用它。因此,True + 1 区分,出于完全相同的原因while True。(并且==仍然不能区分它们,因为优化器不会优化比较。)


同时,如果未对代码进行优化,则解释器最终将在所有这三种情况下进行处理,True并且1完全相同。bool是的子类int,并且从继承其大多数方法int,并且True内部整数值为1。因此,无论您要进行while测试(__bool__在3.x,__nonzero__2.x中),比较(__eq__)还是算术(__add__),无论您使用True还是,都在调用相同的方法1

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.