
Python’s For - Else - federicoponzi
http://book.pythontips.com/en/latest/for_-_else.html
======
askvictor
The word else implies something different to what it does (IMHO at least). In
and if/else clause, the else happens when the primary/expected thing doesn't
happen. I would expect that for a loop, an else clause would happen when the
loop does _not_ finish normally, or if they loop is empty. I'm sure it's a
useful clause in certain cases, but the meaning of the word used is reversed.
It might be better use 'then' or something like that:

    
    
      for i in l:
         print(i)
      then:
         print("iteration finished without breaking")

~~~
rlayton2
I'm a strong python advocate and rarely use for/else for this exact reason. It
makes _more_ sense to me for it to be the other way (your way) around. I
always add a comment on the else when I do too, to ensure that future readers
don't make the same mistake.

~~~
bjd2385
I agree. It's seldom something I've used in my scripts/small projects for this
exact reason.

It should also be noted that there is a `while-else` compound statement as
well, wherein using a `break` immediately exits the loop, without executing
the `else` clause [1].

[1]
[https://docs.python.org/3/reference/compound_stmts.html#the-...](https://docs.python.org/3/reference/compound_stmts.html#the-
while-statement)

------
reuven
I love Python's for-else feature. I've used it on a number of occasions, and
have found it to be useful and to cut down on code.

However, I agree that the use of "else" is horribly confusing. When I teach
"for-else" to students in my Python courses, they are often surprised by the
functionality, and doubly surprised that the word "else" is used. I often joke
that it would be a great feature, if only they had chosen something a bit more
intuitive and memorable instead of "else", such as
"if_we_exited_the_block_without_encountering_a_break". Slightly more
seriously: While I realize that adding a keyword to Python isn't in the cards,
a word such as "nobreak" would be easier for people to understand, and would
stress the point of the functionality.

The fact that Emacs always tries to indent the "else" of a "for-else" to align
with an "if" inside of the loop, rather than the "for" with which it matches,
speaks volumes. I mean, if Emacs is confused, then how are we mere mortals
supposed to remember?

So when I use "for-else", it's almost always in code that I am writing for
myself, and that I don't expect others to read or maintain.

Don't forget that there's also "while-else", although I can't remember the
last time I saw that actually used.

~~~
toxik
I would definitely expect a human to understand code intent (and indent)
better than Emacs. Machine learning has some ways to go still. ;-)

~~~
monsieurbanana
Emacs is really not a reference in terms of code indentation. I expect classic
IDE to be much more robust at it, since emacs uses regex most of the time to
determine indentation (the javascript mode is the only exception I remember),
whereas I imagine a full-blown python IDE will actually parse the code.

------
mixmastamyk
Yes the for...else is useful but for years it didn't do what I expected, which
is execute when the sequence was empty. After reading the docs a half dozen
times I finally internalized that the else actually means "not broken," aka
"not found."

So now when I use it, I always put a comment next to it for myself and the
next person:

    
    
        for item in items:
            if found:
                break
                
        else:  # no-break 
            print('not found.')

~~~
iamforreal
Honestly, I would just not use it wherever possible. It's a wart on the
language, and there are plenty of semantically superior alternatives. I would
rather wrap in function + use return over break, use a sentinel value, or
raise an exception over using the else block.

If there's anything I've learned, it's that just because a language has a
feature, doesn't mean it should be used.

~~~
maxbond
Function + return seems like a good solution, but I'd rather not keep
additional state or use `raise` (ie socially acceptable GOTO) when the
language has a primitive for this.

You shouldn't use a feature just because a language has it, you should use it
because it's a simple, well-defined primitive that's less complex than
alternatives.

------
nickm12
[https://twitpic.com/4a52sh](https://twitpic.com/4a52sh)

A survey on this feature was done at Pycon 2011. Maybe things have changed
since then, but at the time, most people didn't understand what for/else did.

I think it's a bad construct because, as many have mentioned, a lot of people
make the commonsense interpretation that the else block will only execute if
the for block did not.

Note, the link above come via Jeremy Avnet in the comments of this blog post.

[https://nedbatchelder.com/blog/201110/forelse.html](https://nedbatchelder.com/blog/201110/forelse.html)

~~~
rplnt
> the commonsense interpretation that the else block will only execute if the
> for block did not

That is exactly what it does. So when you explicitly break, the for body was
executed. As for the poll, the first answer is not wrong. It's first option
even, the question is vague enough. I wouldn't read that much into it, 33+24+9
(66%) people gave correct answer.

~~~
nickm12
I think you're mistaken on both counts. The first answer is what I call the
"commonsense interpretation" and it is incorrect about what python's for/else
does.

~~~
rplnt
I don't know what to tell you...

For break to be called explicitly it has to enter the loop body - there is no
other way. The break only works in the loop body.

And the first answer is correct in the sense that it is not wrong (try it).
The poll is simply flawed.

------
SmirkingRevenge
I've used the loop -> else construct sparingly - there are times when it is
really convenient.

But it is VERY confusing.

Its very confusing, and poor design - IMHO - because the presence of an "else"
block, in the typical case, generally indicates that the preceding block was
never executed.

When it comes to while/for blocks in python, the keyword literally indicates
the opposite - that the preceding block executed without hiccups - and so also
execute this block.

Its a great feature - but there should have been another keyword for it:
"also" (or something like that)

~~~
wwwtyro
Hold up. Stop. Read the parent closely.

This is a case where the right answer is only obvious once it's been stated.

> Its a great feature - but there should have been another keyword for it:
> "also"

SmirkingRevenge - !@#$ing brilliant. You've stepped outside the box and
presented an answer that will click as soon as it's read.

Someone make a PEP.

~~~
hueving
"nobreak" also makes as much sense. Even Raymond Hettinger has suggested that,
but getting a new keyword at this point in python is going to be really
difficult.

~~~
SmirkingRevenge
"nobreak" also works, and arguably has less ambiguity than my suggestion.

And I agree getting a new keyword for such a limited use-case feature would be
difficult and doesn't feel likely.

------
eigenvalue
The fact that so many commenters here are confused or surprised by what it
means or how it works sort of proves to me that it is a bad idea to use this
in your code-- especially if anyone else might need to work on that code. I
always prefer the clearer, obvious approach, even it is slightly longer.

~~~
kbumsik
It is true that this is confusing for people who come across it for the first
time, but it is easy to understand/remember once you know this feature.

~~~
genidoi
A counter-intuitive feature is not worth using even if 9/10 python devs know
about it. Which they don't seem to.
[https://twitpic.com/4a52sh](https://twitpic.com/4a52sh)

Seems like bad usability.

------
contravariant
Oh, that didn't do what I expected at all.

I was expecting this to be similar to:

    
    
        if list: for x in list:
           # Some code
        else:
           # else clause
    

which would have been quite useful to have for general iterators. But
apparently it's a statement that triggers _unless_ you use 'break' to end the
loop, which just seems like a really weird use case, and smells like a goto.

~~~
xiaq
It is useful when the "normal" flow is to terminate the loop early with a
break. For instance, in a linear search algorithm:

    
    
        for x in li:
          if x == to_find:
            print("found")
            break
        else:
          print("not found")
    

Logically, the "else" matches with the "if" introducing the "break" and means
"if none of elements cause the break branch to be taken".

~~~
contravariant
Yeah I get that it can be useful, but I just don't like ending a loop in two
different ways. It just seems too easy to end up writing something like:

    
    
        for x in sorted_list:
            if x == to_find:
                print("found")
                break # we've found a match
            elif to_find > x:
                break # match is not possible
        else:
            print("not found)

~~~
fyi1183
Funny, I actually like for-else because it can _simplify_ loop exits.

First of all, the core of a linear search loop always has two exits: the break
and the normal (unsuccessful) loop exit.

But in a lookup-or-insert pattern, a regular for loop has two possible states
that the system can be in at the end of the loop. With a for-else loop, you
can put the or-insert part into the else block, so that there is a simpler
invariant: after the loop construct, the element always exists and has been
found.

I was honestly surprised by all the negativity in this thread. To offer an
alternative opinion, there have been plenty of times when I missed having for-
else while writing C.

~~~
gvx
I feel the same way. For-else is one of those things that make me miss Python
when I'm working in another language, alongside things like list/set/dict
comprehensions, Python's object model and it's way of dealing with generators
and iteration.

------
benwilber0
I hate adding syntax but you know what's really useful? Django's `for ..
empty` template syntax.

I would like to see this in Python:

    
    
      for i in []:
        print(i)
      empty:
        print("Sequence was empty!")

~~~
askvictor
Write up a PEP! It's a great idea. Syntax can be good if it makes sense and
makes code easier to read and write.

~~~
mixmastamyk
Won't be accepted. Recognized as a problem, but now set in stone.

~~~
askvictor
Maybe in python 4...

~~~
mixmastamyk
Nope, been discussed many times on the lists, neither planned.

------
jacob019
I've been using this for years and never thought twice about it, I'm shocked
by all the negativity.

It's great for controlling iteration in a nested loop:

    
    
      for item in report():
          for event in in item['events']:
              if event in events_we_care_about:
                  break
          else:
              continue
          act_upon(item)
    

I realize it's a trivial example that could be written without a nested loop,
but there are often good reasons for nested loops and littering them with
control variables and if statements gets messy quick. I have always found for-
else to produce more elegant code.

Your criticisms may be quite valid, I just wanted to share my surprise.

~~~
ankrgyl
I may be missing something... but how is that clearer than

    
    
      for item in report():
          for event in item['events']:
              if event in events_we_care_about:
                  act_upon(event)
                  break

~~~
jacob019
It's a trivial example. Just to demonstrate how for else helps to control an
outer loop from within an inner loop.

------
jordansmithnz
To me, the else isn’t intuitive if you haven’t encountered it before. It’s a
nice feature, but perhaps better naming could improve code readability.

How about for-finally? It’s a hard thing to name correctly, but to me this
seems to be slightly more intuitive if you’ve never seen the feature before.

~~~
makecheck
You can’t use "finally" because that implies an expression that runs
regardless, which this does not (it only runs if the loop never breaks).

~~~
jordansmithnz
Yeah, that’s a good point. To me it’s a little more clear than ‘else’ but
there might be a better name still that doesn’t suggest this code always runs.
Naming is hard :)

~~~
mmebane
Perhaps `for...lastly...else`? `lastly` being the current meaning of `else`
(something that happens after the iteration has completed), `else` being the
thing I wish it meant (something that happens when the iterator was empty).

------
hiccuphippo
I had my first legitimate use of for else last week after knowing about it for
years and it (the keyword) never making much sense.

I had to call an API for it to load data into a cache, but sometimes this
wouldn't work so I made it try up to 3 times, otherwise it would show an
error. So something like:

    
    
        for i in range(3):
            if load_cache():
                log("success")
                break
        else:
            log("failure")
    

Else made total sense here.

------
zmmmmm
Nearly everybody seems surprised by what it does, which would appear to
constitute a fairly strong violation of the "principle of least surprise" that
is one of the most cited guiding principles employed by the designers of
Python. As such it would seem better to be described as an "anti-tip" or
something like that.

~~~
eesmith
It was a feature van Rossum added in the earliest days of Python, on 14 Oct
1990, back before he fully developed his design sense.

As I recall, he said added it because the implementation structure of the
"for" code matched the implementation structure of the "if" code, and he felt
that 'for' could use an 'else'.

------
kbumsik
It is worth noting that the else clause can also be used with while loops.

[1]:
[https://docs.python.org/3.6/reference/compound_stmts.html#wh...](https://docs.python.org/3.6/reference/compound_stmts.html#while)

~~~
kasbah
Seems a bit useless since

    
    
        while x < 2:
            ...
        else: 
            print('>= 2')
    

is the same as

    
    
        while x < 2:
            ... 
        print('>= 2')
    

unless you use a break. Just seems unnecessarily confusing to me. I have used
for ... else though.

~~~
znpy
I think that the subtle difference is that in the second example "print('>=
2')" is always executed whereas in the first example the print statements is
executed only if no break was executed in the while loop.

Quoting documentation: "A break statement executed in the first suite
terminates the loop without executing the else clause’s suite."

~~~
kasbah
I did say "unless you use a break". I don't think this is very useful.

------
makecheck
While this is a great capability to have, the tendency to have an indented
“if” preceding a not-indented “else” just always seems like somebody might
have made a mistake.

I would rather something that captures the loop state itself as an operable
object, such as:

    
    
        with for as loop:
            for x in my_list:
                ...
            if not loop.broken:
                ...

~~~
sooheon
Interesting how people's intuitions differ. For me hiding yet another . method
that requires discovery is more burden on the human. I'd rather reach for
map/filter => processing.

------
m4r71n
One thing that for-else is very useful for is implementing an elegant and
easy-to-understand retry mechanism:

    
    
      for _ in range(3):
          success = do_something()
          if success:
              break
      else:
          raise FailedToDoSomething
    

I generally like this feature of the for loop and it would be a shame if it
was removed.

~~~
cup-of-tea
Indeed I have use this idiom a lot. The alternative is to store a state
variable indicating success which isn't very nice.

------
blumomo
I find this pattern very much better expressed with functional programming:

    
    
      item = next((item for item in container if search_something(item)), None)
      if item:
          process(item)
      else:
          not_found_in_container()
    

instead of as suggested in the blog post:

    
    
      for item in container:
          if search_something(item):
              # Found it!
              process(item)
              break
      else:
          # Didn't find anything..
          not_found_in_container()

~~~
gvx
Your suggestion contains bugs, though. To make it fully equivalent to the code
in the blog post, it would need to be:

    
    
      sentinel = object()
      item = next((item for item in container if search_something(item)), sentinel)
      if item is not sentinel:
          process(item)
      else:
          not_found_in_container()
    

... because item could be None or another falsy value.

------
SamReidHughes
Since Python doesn't have goto statements, this removes one of the situations
where a goto statement is the best tool to use in a language like C++.

~~~
xapata
In a sense, raise/except is a goto.

Go-lang decided against exceptions in part because they are equivalent to
gotos.

~~~
zephyrfalcon
But Go does have a goto statement...?

~~~
Vendan
And like many languages that do, it's an extreme code smell to use it, whereas
most(if not all?) languages that have exceptions recommend to use them. Though
technically, Go does have exceptions (under the name "panic"), just not a very
easy to use "try/catch" block (the "try/catch" stuff is function level, so you
have to do anonymous functions to "emulate" other language's try/catch). Also,
Go's goto has restrictions.

~~~
SamReidHughes
It's not really a code smell. They just don't let newbies use it in CS 101
because they want them to think in a structured manner about loops and such.

------
hajderr
This is strongly advised against in the book Effective Python as the semantics
are opposite to what one is used to. The else clause will always be executed
unless there's a 'break' in the for-loop.

------
marban
Now I feel like I have to rewrite every single Python script I ever wrote.

~~~
vesinisa
Please don't bother. Obscure, very rarely used language constructs like these
often don't really enhance the code's maintainability. On the contrary; would
you have understood the very same code yourself _before_ reading this article
if someone _else_ had used the construct?

~~~
AlphaSite
I think if you read it, you'd read it and get it.

------
pure_ambition
Well, that’s a dumb feature. Very counterintuitive, requires you to stop and
think, and there’s a high probability that it’ll be misunderstood at first
glance. I hate things like this - you’ll read it, think you understand it,
experience a bug, spend hours debugging, then finally look up the
documentation for ‘else’ and see that it means something different from what
you thought.

------
julienfr112
I write quite often something like that:

    
    
        for i in range(10):
          for j in range(10):
            if condition(i,j):
              dosomething
              break
          else:
            continue
          break
    
    

It's a pattern that ensure that dosometing is run at most once by
"propagating" the break to the outer loop.

The alternative is use a function and do a return.

Any though of this ?

~~~
alkonaut
I'm not a python programmer but I find the last 4 lines rather hard to read.
Unless there is a performance reason to not do so, I'd argue that you are
iterating over _pairs_ (i,j) here and I'd make the code clear. There are 100
pairs, and once you find one that satisfies the condition you are done. Again,
not a python programmer but it might look like

    
    
      (x, y) for x in range(10) for y in range(10)
         if condition(i,j)
            dosomething;
            break;
    

Edit, ("zip" is not the outer product... )

~~~
quietbritishjim
The relevant function is itertools.product() [1]. It can give the Cartesian
product of arbitrary iterables, but if you have the same iterable more than
once, as in this example, you can take advantage of the repeat= optional
parameter to simplify the call a little bit:

    
    
        for x, y in itertools.product(range(10), repeat=2):
            if condition(x, y):
                dosomething(x, y)
                break
    

Alternatively, you can use Python list comprehensions:

    
    
        prodlist = [x, y for x in range(10) for y in range(10)]
        for x, y in prodlist:
            if condition(x, y):
                dosomething(x, y)
                break
    

Overall, I still prefer refactoring into a function and using return, as the
GP comment suggested.

[1]
[https://docs.python.org/3/library/itertools.html#itertools.p...](https://docs.python.org/3/library/itertools.html#itertools.product)

------
fratlas
I'm in favour of writing simple python that even a junior dev can read rather
than using niche syntax tricks. Guess that depends on whether you think a
junior should know this syntax.

~~~
ben0x539
This is a weird response. You can definitely argue that it's bad syntactic
design because people seem to expect it to do something different, but if the
argument is just that some category of programmer doesn't already know what it
does, you're pretty much arguing against introducing any new language feature,
syntax or library that wasn't taught in your imagined CS101 or whatever.

~~~
ben509
> you're pretty much arguing against introducing any new language feature,
> syntax or library that wasn't taught in your imagined CS101 or whatever.

But there clearly is a body of knowledge which you can discover yourself: you
can interview candidates, read discussions on SO, read library code, and you
will find some techniques are commonly used, and some are rarely used.

That distribution of what's known is changing over time regardless of what we
do, because our peers are constantly learning and studying at different rates.

This feature has been around for years, and is so rarely used that only
language mavens know about it. Thus, you can observe that this is a failed
feature and avoid using it.

We can always reevaluate if people understand it later on, since that could
change.

~~~
fyi1183
On the other hand, when you do run across this construct in the wild, it is
trivial to look up what it does and learn. I imagine that's how most people
learned about it, since it doesn't exist in other languages.

In a way I find your comment contradictory: on the one hand, you appreciate
and embrace that the body of common knowledge can change. On the other hand,
you use the current state of common knowledge as an argument against doing
things that can improve the common knowledge.

~~~
Vendan
As a counterpoint, a similar syntactic construct exists in several templating
languages, and behaves radically different...

------
joshmkay
I can't see this ever being used. As a non-python programmer, if I ran across
this it would be very confusing. If it's simple enough to write the same code
without the else block and have your code be more readable, why not do that?

------
OJFord
I much more frequently wish that I could express 'or do this if zero
iterations' than I use for-else as it is; I do so wish it had the former
semantic.

That and the absence of do-while are the cause of a good deal of non-DRY
python around loops.

------
ageofwant
The `else` clause follows the same general pattern wherever it supported:
`for`, `while` and `try` are consistent. It usually makes code shorter or at
least leads to narrowing concerns when used with `try`.

For some reason I often forget to use it though.

------
holografix
I would never include this in my code. It’s a construct most devs have never
seen in Python and it differs from anything I’ve seen in other languages.

Always KISS.

------
AlexCoventry
I'm curious why people are interested in this. No way I would have predicted
it would get to the top of HN.

------
nearmuse
Or maybe it was a terrible "or else" pun that does not deserve much
discussion.

------
quickthrower2
Else is a bit of a confusing name for the keyword IMO.

How about "ifcompleted"

------
rad_gruchalski
Pretty convenient feature.

------
grosjona
I hate it when code uses weird niche features that are not obvious, are
impossible to look up and add 0 value aside from saving a couple of lines of
code.

~~~
vortico
[Mistake, a moderator please delete this comment! Completely agree with the
parent comment now. for...else is dumb and unintuitive. I misunderstood its
function, which proves the above point to me.]

~~~
Izkata
> The symantics of for...else are immediately obvious

The code in your example is how it reads, but that's not how it works. The
"obvious" reading is wrong, so it's obviously not "immediately obvious".

This is what it's actually doing:

    
    
      found = False
      for l in get_list():
        if ...:
          found = True
          break
      if not found:
        ...

------
bjourne
This is coincidentally my highest rated Stackoverflow answer ever. :)

[https://stackoverflow.com/questions/9979970/why-does-
python-...](https://stackoverflow.com/questions/9979970/why-does-python-use-
else-after-for-and-while-loops)

Tl;dr my highly opinionated opinion is that one should never use the construct

------
cup-of-tea
For-else is nothing. The really weird one is the Try-else.

The For/While-else does exactly what I expect. I'm not sure why others find it
confusing.

The Try-else does make some code more readable but is quite rare I would say.

~~~
Vendan
Well, for one thing, a few templating engines have a "for each item, print x,
else if there are no items, print y". (for instance:
[https://handlebarsjs.com/builtin_helpers.html#iteration](https://handlebarsjs.com/builtin_helpers.html#iteration))

~~~
cup-of-tea
That's not intuitive at all. That's equivalent to:

    
    
        if len(items) == 0:
            print(y)
        else:
            for item in items:
                print(item)
    

Which means there's a whole extra implicit if. The for in Python is really
this:

    
    
        it = iter(items)
        while True:
            try:
                item = next(it)
            except StopIteration:
                print("else")
                break
            print(item)
    

Where that try/except is like "if there is a next item do this, else do this",
hence:

    
    
        for item in items:
            print(item)
        else:
            print("else")

~~~
Vendan
Never said intuitive, but that construct is EXTREMELY useful for things like
"no results found" and such, when a list of items is empty. Therefore, it is
usually explained quite early and used often in template languages that have
it. This is the first time I've seen python's, after dealing with several
large codebases written in python.

------
mychael
Never use this in the real world please.

------
random_throw
This is discussed as item 12 in Effective Python. The author argues that you
should avoid "else" blocks after "for" and "while" loops. The main argument is
that the behavior is not intuitive and could be confusing upon reading given
its somewhat obscure usage.

He suggests using a helper function with an early exit or setting a result
variable and using a break in your loop instead.

