[Python-ideas] Control Flow - Never Executed Loop Body
Steven D'Aprano
steve at pearwood.info
Fri Mar 25 00:05:19 EDT 2016
On Tue, Mar 22, 2016 at 02:49:44PM -0400, Terry Reedy wrote:
> On 3/21/2016 9:29 AM, Steven D'Aprano wrote:
> >On Sun, Mar 20, 2016 at 11:19:54PM -0400, Terry Reedy wrote:
> >>On 3/20/2016 9:32 PM, Steven D'Aprano wrote:
> >>
> >>>I understood from the keyword that "else" ran *if the for block
> >>>didn't*, i.e. when the loop iterator is empty.
.................................^^^^^^^^
I just realised I typo-ed that. I meant "iterable", not iterator.
For-loops can iterate over any iterable (sequences, sets, dicts etc) not
just iterators (as I'm sure you know).
I may have inadvertently given you the wrong impression, in which case,
I apologise.
> >>>A perfectly natural mistake to make,
>
> >>Why is the truth a mistake?
> >
> >It's not the truth.
>
> Yes it is. When the iterator is empty, possibly after many iterations,
> and the iterator is tested, the else clause runs.
I can only guess that you thought I was referring to the invisible,
implementation-dependent iterator which is used internally by the
for-loop. (See byte code below for what I mean.) That's fine, but it
isn't what the topic of this discussion is about. It's irrelevent.
Nobody should care about whether *that* iterator is exhausted or not,
because that iterator is unreachable from Python code.
What we might care about is the iterable "seq" used in (for example)
"for x in seq". Sometimes people want to run a block of code *only* if
the body of the for loop never runs, and some people mistakenly thought,
or think, that the "else" clause does that.
I know this for a fact because I was one of those people, and I have
seen others make the same mistake. I can prove it is a mistake by
running this code:
seq = [2, 4]
for x in seq:
print("seq is not empty") # this is correct
else:
print("seq is empty") # this is wrong
assert list(seq) == []
The assertion won't *always* fail, e.g. try passing "seq = []") but the
fact that it *sometimes* fails proves that it is a mistake to interpret
the "else" clause as "run only when the for loop doesn't run".
Take this for-loop:
def f():
for x in seq:
print(x)
else:
print(1)
In Python 2.7, dis.dis(f) gives:
2 0 SETUP_LOOP 24 (to 27)
3 LOAD_GLOBAL 0 (seq)
6 GET_ITER
>> 7 FOR_ITER 11 (to 21)
10 STORE_FAST 0 (x)
3 13 LOAD_FAST 0 (x)
16 PRINT_ITEM
17 PRINT_NEWLINE
18 JUMP_ABSOLUTE 7
>> 21 POP_BLOCK
5 22 LOAD_CONST 1 (1)
25 PRINT_ITEM
26 PRINT_NEWLINE
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
My guess is that you thought I was referring to the iterator which is
created by the call to GET_ITER, and iterated over by FOR_ITER. Mea
culpa -- I was not. I meant to say iterable, not iterator, and in this
example I would mean "seq".
If you look at the byte code, you will see that there is no test for
whether this internal iterator, or seq for that matter, is empty. When
the for loop, the code unconditionally executes the else block. If the
for loop contains a break (or a return, or a raise) execution may
transfer to outside of the for...else altogether, skipping the else
block, but there is still no test for emptiness. It's an unconditional
jump: if you reach the break, you jump past the end of the for...else.
> > The else block does *not* only run if the for block doesn't run.
>
> I have never, ever, claimed that. Initially empty and empty after all
> items have been processed are different.
Then I hope that we now are on the same page, and agree that:
(1) "else" does NOT mean "execute this block only if the for block
doesn't run", even if some people mistakenly did, or do, think so.
(2) Some people want such a statement, and that's the topic for this
discussion.
(3) As for...else now exists, there is no testing whether or not the
for-loop ITERABLE is empty, or whether the internal (inaccessible)
ITERATOR is empty; furthermore, other implementations or versions may
not even use an internal ITERATOR (e.g. Python 1.5 certainly doesn't).
(4) "break" doesn't set a flag, it just jumps to the outside of
for...else (although in principle this is implementation dependent,
there is nothing in the semantics of for...else that *requires* a flag
be set), or the iterable be tested for emptiness.
--
Steve
More information about the Python-ideas
mailing list