On Tue, Nov 18, 2014 at 6:01 PM, Chris Angelico <rosuav@gmail.com> wrote:
On Wed, Nov 19, 2014 at 12:15 PM, Steven D'Aprano <steve@pearwood.info> wrote:
>> and any use of a loop that appends to a list is rightly
>> considered code smell.
>
> I'm afraid I don't understand that comment. Why is appending to a list
> inside a loop a code smell? That's exactly what list comps do.

That's precisely why. If I write code like this:

l = []
for i in something:
    l.append(func(i))

then I should rework it into a comprehension. Having a filter doesn't
change that:

l = []
for i in something:
    if i:
        l.append(func(i))

That's still possible with a list comp, and should be rewritten as
one. But having a break in there *does* change it, because there's no
way in the language to do that. The question is: Is it better to abuse
StopIteration or to turn the list comp back into an explicit loop? And
if anyone chose the former, their code will break.

Not everything you do with an explicit loop can be done with a comprehension, and that's by design. Comprehensions should be easier to reason about than code using for-loops. And generator expressions should work the same way, except for producing results in a lazy fashion.

The StopIteration hack breaks this equivalence and hampers the ability to reason, since you can't tell whether a predicate might raise StopIteration.

It was never my intention that generator expressions behaved this way -- it was an accidental feature that surprised me when it was first shown to me, and I've never gotten used to it. (And I don't care whether you say it is "obvious", call it "stop()", and only use it in an "idiomatic" fashion -- it's still a surprise for anyone who has to debug code involving it.)

The only thing standing in the way of fixing this is the recognition that there may be a fair amount of code out there that depends on this hack, and which will have to be rewritten.

--
--Guido van Rossum (python.org/~guido)