Python’s for/else construct

I’ve been a Python program for a few years now, but I’ve only just encountered the ‘for/else’ construct. I was changing a conditional to a for loop, but accidentally left behind the else block. My initial reaction after looking back at the code was, “Oh, the else must run if the ‘for’ block never executes,” because that would actually make sense. That’s not what it does though, I thought, because the code in the ‘for’ does execute. I wrote some test code, just be sure I’m not crazy:

def test(l):
    for x in l:
test([1, 2])
# else
# 1
# 2
# else 

And lo, both blocks execute — the loop body and the else block. At this point, I took to the interwebs, and, naturally, someone has described this bizarre python behavior. The else will not execute if you break before the iterator is exhausted. Even knowing this construct exists and knowing I’ve been is situations where it would be usable, I don’t think I would use it. Considering that I introduce loops on many occasions where before I was checking for None, an ‘else: …’ like that in the code above would always initially look like a programming error, and I’d have to spend precious seconds of my time unpacking the intent of that code.

I’m not the first to express surprise at the behavior of this feature of Python, and one helpful redditor linked to one of Raymond Hettinger‘s[1] lecture videos which gives historical context for why the construct exists and why it’s named the way it is. The explanation is cogent and helps me to see for/else less as a misfeature and more as an unfortunately named, but effective construct, much like the ‘try/else’ which I had encountered in some FOSS code about a year back.

1: Fun fact: Raymond Hettinger also wrote my favorite and most-referred-to Python article on the super() construct. It helped open up my thinking on mix-in types, which I now use readily to graft functionality onto my Python objects and classes.

Leave a Reply