Python’s “else” clause for loops

Let’s say that we have a list of tuples, with each tuple containing some numbers. For example:

>>> mylist = [(3,5), (2,4,6,8), 
(4,10, 17), (15, 14, 11), (3,3,2)]

I want to write a program that asks the user to enter a number. If one of the tuples adds up to the user’s input, we’ll print the tuple out. Sounds good, right? Here’s how we could write it:

>>> lookfor = int(input("Enter a number: "))
for one_tuple in mylist:
if sum(one_tuple) == lookfor:
print(f"{one_tuple} adds up to {lookfor}!")

In other words: We iterate over the list of tuples, and use the built-in “sum” function to sum its contents. When we find a tuple whose sum matches the user’s input, “lookfor”, we print the tuple.

And sure enough, if we run this short program with our previously defined “mylist”, we get the following:

Enter a number: 8
(3, 5) adds up to 8!
(3, 3, 2) adds up to 8!

But what if the user enters a number, and no tuples match that sum? We don’t say anything; we basically just leave the user hanging. It would be nice to let them know that none of the tuples in our list matched their sum, right?

This can traditionally be done using a “flag” variable, one whose value starts off with a “False” value (i.e., we haven’t found what we’re searching for) and which is set to “True” if and when we do find what we want. Here’s how we could do that in Python:

found = False
lookfor = int(input("Enter a number: "))
for one_tuple in mylist:
if sum(one_tuple) == lookfor:
found = True
print(f"{one_tuple} adds up to {lookfor}!")
if not found:
print(f"Sorry, didn't find any tuple summing to {lookfor}")

And sure enough, this works just fine:

Enter a number: 3
Sorry, didn't find any tuple summing to 3

Python provides us with a different way to handle this situation. It’s a bit odd at first, and newcomers find it hard to understand — but in cases like this, it helps to shorten the code.

What I’m talking about is the “else” clause for loops. You’re undoubtedly familiar with the “else” clause for “if” statements. You know, the block that gets executed if the main “if” clause isn’t true.

With a loop — and this works with both “for” and “while” loops — the “else” clause is executed if the loop reached its natural end. That is, if Python didn’t encounter a “break” statement in your loop, then the “else” clause will work. This allows us to remove the “found” flag variable, as follows:

lookfor = int(input("Enter a number: "))
for one_tuple in mylist:
if sum(one_tuple) == lookfor:
print(f"{one_tuple} adds up to {lookfor}!")
print(f"Sorry, didn't find any tuple summing to {lookfor}")

In other words: If we found a tuple that adds up to “lookfor”, then we print it out and exit the loop immediately, using “break”. But if we reach the natural end of the loop (i.e., never encounter a “break”), then the “else” clause is executed.

Aha, but you might see a problem with our implementation: Whereas in the first and second versions, we printed all of the matching tuples, in this version, we only print the first matching tuple. Even though two tuples have sums of 8, our “break” statement exits the loop once it finds the first one.

The “else” clause on a loop is thus only useful if you’re planning to exit out of it with a “break” statement at some point. If there’s no “break”, then there’s no real value in having an “else” statement.

Also realize that there’s a difference between the code in an “else” block and the code that immediately follows a loop. Code in the “else” block will only execute if the “break” wasn’t encountered.

I’ve found that many newcomers to Python are confused about these “else” blocks, partly because they expect “else” to be connected to an “if”. They thus try to align the “else” with the “if” statement’s indentation, which doesn’t work. In many ways, I do wish that Python’s core developers had chosen a different word, rather than reusing “else”… but that didn’t happen, and so now we have to learn that “else” can be used in two different contexts.

  • Mikael says:

    … not to mention the use of ”else” in ”try-except”.

    • Good point — that’s a different use altogether; I’ll try to write something about that, too!

  • >