Understanding while...else...

Terry Reedy tjreedy at udel.edu
Tue Jan 22 12:44:05 EST 2013


Several people have trouble understanding Python's while-else and 
for-else constructs. It is actually quite simple if one starts with 
if-else, which few have any trouble with.

Start with, for example

if n > 0:
   n -= 1
else:
   n = None

The else clause is executed if and when the condition is false. (That 
the code is useless is not the point here.) Now use pseudo-Python label 
and goto statements to repeatedly decrement n

label: check
if n > 0:
   n -= 1
   goto: check
else:
   n = None

The else clause is executed if and when the condition is false. (I am 
aware that the above will always set n to None if it terminates normally 
and that something more is needed to do anything useful, but this is not 
the point here.) Now use a real Python while statement to do the *same 
thing*.

while n > 0:
   n -= 1
else:
   n = None

The else clause is executed if and when the condition is false. This is 
the same as with the non-problematical if statement! To see that the 
pseudo-Python is the 'correct' expansion, we can look at the 
disassembled CPython byte code.

from dis import dis
dis('if n > 0: n -= 1\nelse: n = None')
dis('while n > 0: n -= 1\nelse: n = None')

produces

   1           0 LOAD_NAME                0 (n)
               3 LOAD_CONST               0 (0)
               6 COMPARE_OP               4 (>)
               9 POP_JUMP_IF_FALSE       25
              12 LOAD_NAME                0 (n)
              15 LOAD_CONST               1 (1)
              18 INPLACE_SUBTRACT
              19 STORE_NAME               0 (n)
              22 JUMP_FORWARD             6 (to 31)

   2     >>   25 LOAD_CONST               2 (None)
              28 STORE_NAME               0 (n)
         >>   31 LOAD_CONST               2 (None)
              34 RETURN_VALUE

   1           0 SETUP_LOOP              32 (to 35)
         >>    3 LOAD_NAME                0 (n)
               6 LOAD_CONST               0 (0)
               9 COMPARE_OP               4 (>)
              12 POP_JUMP_IF_FALSE       28
              15 LOAD_NAME                0 (n)
              18 LOAD_CONST               1 (1)
              21 INPLACE_SUBTRACT
              22 STORE_NAME               0 (n)
              25 JUMP_ABSOLUTE            3
         >>   28 POP_BLOCK

   2          29 LOAD_CONST               2 (None)
              32 STORE_NAME               0 (n)
         >>   35 LOAD_CONST               2 (None)
              38 RETURN_VALUE

The while loop code adds SETUP_LOOP to set up the context to handle 
continue and break statements. It also add POP_BLOCK after the while 
block. I presume this disables the loop context. Most importantly for 
this discussion, JUMP_FORWARD (past the else block), which is implicit 
in if-else statements, changes to JUMP_ABSOLUTE (to the condition test), 
which is implicit in while statements and explicit in the pseudo-Python 
expansion. Everything else is the same -- in particular the 
POP_JUMP_IF_FALSE, after the condition test, which in both cases jumps 
to the else block. So in both statements, the else block is executed if 
and when the condition is false.

As for for-else statements, a for loop is basically a specialized while 
loop plus assignment. The implicit while condition is that the iterable 
has another item to process. So the else clause executes if and when 
that condition is false, when iter(iterable) is exhausted.

-- 
Terry Jan Reedy




More information about the Python-list mailing list