Ned Batchelder: Loop Like A Native

Steven D'Aprano steve+python at pearwood.info
Sun Aug 7 21:46:48 EDT 2016


On Mon, 8 Aug 2016 09:19 am, Rick Johnson wrote:

> On Saturday, August 6, 2016 at 10:43:01 PM UTC-5, Steven D'Aprano wrote:
> 
>> Yes. The two ways of ending the loop are distinct and different:
>> 
>> - reach the end, and stop;
>> - bail out early.
>> 
>> 
>> When you read a book, there are two ways of stopping:
>> 
>> - reach the end, and run out of pages to read, so you stop;
>> - give up reading early, and just put the book away.
>> 
>> (Or possibly throw the book across the room.)
>> 
>> 
>> Why would you treat these two cases in the same way?
>> 
>> interested = True
>> for page in book:
>>     if interested:
>>         read(page)
>>     if bored_now():
>>         interested = False
> 
> This algorithm does not exit early when `interested` becomes false,
> instead, it iterates every page of the book regardless of the value of
> `interested`. Was this intentional, or merely a mistake?

I said: "Why would you treat these two cases in the same way?", then showed
a code snippet which treats the two cases in the same way (i.e. iterates
over every page of the book). Of course it was intentional.


> 
>> finished = False
>> while not finished:
>>     try:
>>         page = next(book)
>>     except StopIteration:
>>         finished = True
>>     else:
>>         read(page)
>>         if bored_now():
>>             finished = True
> 
> That kind of code, whilst being "somewhat" idiomatic python, is horrific.

Of course it is horrific. And I don't think it is even the slightest bit
Pythonic. There may (very unlikely, but theoretically possible) be
occasions where you have no choice but to write code like this, but if so
you wouldn't describe it as idiomatic Python code.


> If the intent is to iterate the pages of a book until the end is reached
> *OR* until the "reader's interest wanes", then a simple for-loop will
> suffice.

That's exactly the sort of thing that Lawrence is objecting to, because (and
I quote the part of my email you deleted):

"A loop like [example with break] actually has two different ways to
terminate. Is there any good reason for them to be written two different
ways?"


> for page in book:
>     isReaderInterested = reader.read_page(page)
>     if not isReaderInterested:
>         break
> 
> Simple is better than complex.

Precisely. I'm showing the horrible code you have to write to avoid writing
two different ways of terminating a loop with two fundamentally different
ways of terminating.

Lawrence, if you're still reading, I hope I am not misrepresenting your
position here. If you think that there is an alternative way of writing
this code which avoids two exits from the loop but without having to write
such unidiomatic (if not idiotic) code, please tell us what you would do.


There is one clean way for doing this: use itertools.

def interesting(page):
    """Return True if the page is interesting, False if boring."""

for page in itertools.takewhile(interesting, book):
    reader.read_page(page)


but it's not always easy to re-write the code to match that idiom.




-- 
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