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

这个问题引起了有关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中的布尔值评估方式是否有变化?

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?


回答 0

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

In Python 2.x, True is not a keyword, but just a built-in global constant that is defined to 1 in the bool type. Therefore the interpreter still has to load the contents of True. In other words, True is reassignable:

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

In Python 3.x it truly becomes a keyword and a real constant:

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

thus the interpreter can replace the while True: loop with an infinite loop.


回答 1

这不太正确

因此,解释器可以用无限循环代替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:如果您没有打破循环的条件,请使用。

This isn’t quite right,

thus the interpreter can replace the while True: loop with an infinite loop.

as one can still break out of the loop. But it is true that such a loop’s else clause would never be accessed in Python 3. And it is also true that simplifying the value lookup makes it run just as quickly as while 1 in Python 2.

Performance Comparison

Demonstrating the difference in time for a somewhat nontrivial while loop:

Setup

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

Explanation

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

Since True is a keyword in Python 3, the interpreter doesn’t have to look up the value to see if someone replaced it with some other value. But since one can assign True to another value, the interpreter has to look it up every time.

Conclusion for Python 2

If you have a tight, long-running loop in Python 2, you probably should use while 1: instead of while True:.

Conclusion for Python 3

Use while True: if you have no condition for breaking out of your loop.


回答 2

这是一个已有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

This is a 7-year-old question that already has a great answer, but a misconception in the question, which isn’t addressed in any of the answers, makes it potentially confusing for some of the other questions marked as duplicates.

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

>>> True == 1
True

>>> True + True
2

Why does while distinguish the two?

In fact, while isn’t doing anything different here at all. It distinguishes 1 and True in exactly the same way that the + example does.


Here’s 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

Now compare:

>>> 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.)


Now compare 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

Here, it’s emitting a LOAD_CONST (True) for the keyword, which the optimizer can take advantage of. So, True + 1 doesn’t distinguish, for exactly the same reason while True doesn’t. (And == still doesn’t distinguish them because the optimizer doesn’t optimize out comparisons.)


Meanwhile, if the code isn’t optimized out, the interpreter ends up treating True and 1 exactly the same in all three of these cases. bool is a subclass of int, and inherits most of its methods from int, and True has an internal integer value of 1. So, whether you’re doing a while test (__bool__ in 3.x, __nonzero__ in 2.x), a comparison (__eq__), or arithmetic (__add__), you’re calling the same method whether you use True or 1.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。