How coding in Python is bad for you

Steve D'Aprano steve+python at pearwood.info
Sun Jan 29 21:14:30 EST 2017


On Mon, 30 Jan 2017 10:52 am, Erik wrote:

> On 29/01/17 14:42, Steve D'Aprano wrote:
>> 1. for...else is misspelled, and should be for...then;
>>
>> 2. Same for while...else;
> 
> I don't think I'll ever agree with you on this one.
> 
> "then", to me, implies the code following it is always executed.
> "else" implies it's conditional.
> 
> In those constructs it's conditional and therefore, to me, "else" is a
> better reminder of that.

But it isn't conditional. When the code reaches the end of the for/while
block, the else block is ALWAYS executed. Unconditionally.

The only way to skip the else block is to avoid reaching the end of the
for/while block: raise, break or return out of the body of the block.


> It would be even better if it was "else if not break:" to make the
> meaning clearer.

break is not the only way to exit the for loop.


> I would agree that it would be even better than that if 
> it was "then if not break:" (apart from needing the new keyword ;)), as
> then the conditional aspect is explicit.

But it isn't conditional. Your syntax implies that the interpreter keeps
some sort of flag did_we_reach_the_end_of_the_loop_without_break or
something, and then it checks the state of that flag. There is no such
flag. If I remember correctly, that last time this came up here was because
somebody asked how they could read the value of that flag, instead of
setting their own:

for x in seq:
    ...
if not did_we_reach_the_end_of_the_loop_without_break:
    print("break")


They hoped to use the same flag the for-loop used.

But if you use the dis module to decompile the CPython byte-code you will
see that there is no such flag. Code like this:

for i in seq:
    break
else:
    foo
bar

compiles to something like this (results may vary according to the version
of Python):


py> import dis
py> code = compile("""
... for i in seq:
...     break
... else:
...     foo
... bar
... """, "", "exec")
py> dis.dis(code)
  2           0 SETUP_LOOP              19 (to 22)
              3 LOAD_NAME                0 (seq)
              6 GET_ITER
        >>    7 FOR_ITER                 7 (to 17)
             10 STORE_NAME               1 (i)

  3          13 BREAK_LOOP
             14 JUMP_ABSOLUTE            7
        >>   17 POP_BLOCK

  5          18 LOAD_NAME                2 (foo)
             21 POP_TOP

  6     >>   22 LOAD_NAME                3 (bar)
             25 POP_TOP
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE


Not a single condition to be seen, anywhere. Its all unconditional jumps.

To anticipate a possible objection: it is possible that the FOR_ITER
bytecode is implemented with a conditional test, but even so, all that
tests for is whether to enter the main body of the for-loop (10
STORE_NAME ...) or jump to (18 LOAD_NAME ...).

If you remove the break completely, or replace it with raise or (in a
function) return, and you'll see the same structure to the code: for loops
run the main body of the loop, then unconditionally run the else block,
then unconditionally exit the loop and continue on with the rest of the
program. To avoid the else block, you have to jump out of the entire
for-loop.


-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.



More information about the Python-list mailing list