[Python-ideas] Is this PEP-able? for X in ListY while conditionZ:

Andrew Barnert abarnert at yahoo.com
Thu Jun 27 13:28:36 CEST 2013


From: Oscar Benjamin <oscar.j.benjamin at gmail.com>

Sent: Thursday, June 27, 2013 3:16 AM


> On 27 June 2013 08:28, Andrew Barnert <abarnert at yahoo.com> wrote:
>>  Let me try to gather together all of the possibilities that have been 
>> discussed
>>  in this and the two previous threads, plus a couple of obvious ones 
>> nobody's
>>  mentioned.
> 
> You've missed out having "else return" in comprehensions.

For simplicity, I was just dealing with the "break" solutions, not "continue" and "return" (and "raise" or anything else anyone might want). I should have said that, but I didn't; sorry. So, this goes under case 8.

But it's worth noting that return adds additional mental load on top of break. Comprehensions aren't functions, either conceptually or practically, but it will force you to think of them as functions and carry that cognitive dissonance around as you read them. Comprehensions _are_ loops, so break doesn't have that problem.

> I like this
> less than a while clause but it was preferred by some as it unrolls
> perfectly in the case that the intention is to break out of all loops


Some of the ideas can only break out of the last loop, some can only break out of all of them, some can break out of any one loop. I don't think it will make a difference that often in comprehensions. A comprehension with two for clauses and an if is already pushing the limits of readability; throwing in a break or return as well just seems like asking for trouble.

But you're right, that is the difference between else break and else return.

>>  1. Redefine comprehensions on top of generator expressions instead of 

> defining them in terms of nested blocks.
>> 
>>      def stop(): raise StopIteration
>> 
>>      x = [value if pred(value) else stop() for value in iterable]
> 
> I prefer
> 
> x = [value for value in iterable if pred(value) or stop()]
> 

> so that the flow control is all on the right hand side of the "in".


I suppose this solution allows either. Personally, I think using or for non-trivial flow control is more obscure than helpful. Would you write this?

    for value in iterable:
        if pred(value) or stop():
            yield value

Of course not; you'd write:

    for value in iterable:
        if pred(value):
            yield value
        else:
            stop()

>>  Unlike #5, 6, 7, 8, and 10, but like #2, 3, 4, and 9, this only allows you 

>> to break out of one for clause, not any. But that's exactly the same as 
>> break only being able to break out of one for loop. Nobody complains that Python 
>> doesn't have "break 2" or "break :label", right?
> 
> I'm not sure why you expect that it would only break out of one for
> clause; I expect it to break out of all of them. That's how it works
> with generator expressions:

My fault again, I wasn't clear here. What I meant is that #1, 2, 3, 4, and 9 don't give you the option of where or how far to break; #5, 6, 7, 8, and 10 do. The important bit was that I don't think it's that important.

>>  2. Just require comprehensions to handle StopIteration.
>> 
>>  The main cost and benefit are the same as #1.
>> 
>>  However, it makes the language and implementation more complex, rather than 
> simpler.
>> 
>>  Also, the effects of this less radical change (you can no longer pass 
>> StopIteration through a comprehension) seem like they might be harder to explain 
>> to people than the more radical one.
> 
> I think that the current behaviour is harder to explain.

What's hard to explain about the current behavior? StopIteration passes through comprehensions the same way it does through for loops.

>>  7. Add a "magic" while clause that's basically until with the 
> opposite sense.
>> 
>>      x = [value for value in iterable while pred(value)]
>> 
>>  This reads pretty nicely (at least in trivial comprehensions), it parallels 
>> takewhile and friends, and it matches a bunch of other languages (most of the 
>> languages where "when" means "if", "while" means 
>> this).
>> 
>>  But it has a completely different meaning from while statements, and in 
>> fact a barely-related one.
…
>>  Imagine trying to teach that to a novice.
> 
> I can definitely imagine teaching it to a novice. I have taught Python
> to groups of students who are entirely new to programming and also to
> groups with prior experience of other languages. I would not teach
> list comprehensions by unrolling them unless it was a more advanced
> Python programming course.

This makes perfect sense today. Going from novice to intermediate understanding of list comprehensions, and many other areas of Python, is almost trivial, and that's part of what makes teaching Python so much easier than teaching, say, C++.

I've seen hundreds of people show up on StackOverflow and similar places completely baffled by a complex list comprehension in some code they've run into. As soon as you show them how to unroll it, they immediately get it. If that were no longer true, how would you get people over that step?

Also, in my experience, devs coming over from other languages, at least the good ones, want to understand the abstractions, not just use them. They'll want to know why a for clause is just like a for statement and an if clause is just like an if statement but a while clause is nothing like a while statement. Partly this is because many of them are still spending 80% of their time writing in JavaScript or C# or whatever and only 15% in Python, and anything unique about Python that they can't understand, they're going to forget. Comprehensions are flow control expressions that map to flow control statements in a way that's not just simple, but obvious—once you get it, you can't forget it. But if we break the abstraction, that will no longer we true.

>>  8. Allow else break in comp_if clauses.

>> 
>>      x = [value for value in iterable if pred(value) else break]
>> 
>>  This one is pretty easy to define rigorously, since it maps to exactly what 
> the while attempt maps to with a slight change to the existing rules.
>> 
>>  But to me, it makes the code a confusing mess. I'm immediately reading 
> "iterable if pred(value) else break", and that's wrong.
> 
> You wouldn't have that confusion with "else return".


Why not? They're both single-keyword flow-control statements. Why would anyone's brain read else return any differently from else break?


More information about the Python-ideas mailing list