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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to