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

Viewed 98046

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?

3 Answers

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.

Related