From: Terry Reedy on
Tom Machinski wrote:
> In most cases, `list(generator)` works as expected. Thus,
> `list(<generator expression>)` is generally equivalent to `[<generator
> expression>]`.
>
> Here's a minimal case where this equivalence breaks, causing a serious
> and hard-to-detect bug in a program:
>
> >>> def sit(): raise StopIteration()

StopIteration is intended to be used only within the .__next__ method of
iterators. The devs know that other 'off-label' use results in the
inconsistency you noted, but their and my view is 'don't do that'.

From: Gabriel Genellina on
En Sat, 12 Dec 2009 23:43:20 -0300, Ned Deily <nad(a)acm.org> escribi�:
> In article <nad-8CDB63.18012412122009(a)news.gmane.org>,
> Ned Deily <nad(a)acm.org> wrote:
>> In article
>> <ec96e1390912121653w56c3dbe3p859a7b979026bf47(a)mail.gmail.com>,
>> Benjamin Kaplan <benjamin.kaplan(a)case.edu> wrote:
>> > On Sat, Dec 12, 2009 at 7:15 PM, Tom Machinski
>> <tom.machinski(a)gmail.com>
>> > wrote:


>> > > >>> def sit(): raise StopIteration()
>> > > ...
>> > > >>> [f() for f in (lambda:1, sit, lambda:2)]
>> > > Traceback (most recent call last):
>> > > File "<stdin>", line 1, in <module>
>> > > File "<stdin>", line 1, in sit
>> > > StopIteration
>> > > >>> list(f() for f in (lambda:1, sit, lambda:2))
>> > > [1]

>> > > In most cases, `list(generator)` works as expected. Thus,
>> > > `list(<generator expression>)` is generally equivalent to
>> `[<generator
>> > > expression>]`.

>> > Actually, it's list(generator) vs. a list comprehension. I agree that
>> > it can be confusing, but Python considers them to be two different
>> > constructs.

I think nobody has addressed the OP arguments (as I understand them).

First, except the obvious outer delimiters (and some corner cases in 2.x,
fixed in Python 3), a list comprehension and a generator expression share
the same syntax: (x for x in some_values) vs [x for x in some_values].

Also, *almost* always, both list(<comprehension>) and [<comprehension>],
when evaluated, yield the same result [1]. *Almost* because StopIteration
is handled differently as the OP discovered: the list comprehension
propagates a StopIteration exception to its caller; the list constructor
swallows the exception and the caller never sees it.

Despite a promise in PEP 289, generator expressions semantics isn't
explained in detail in the language reference. I can't tell if the
difference is intentional, accidental, undocumented behavior, an
implementation accident, a bug, or what...

[1] <comprehension> being a syntactic construct like:
x**2 for x in range(5)
or:
f() for f in [lambda:1, sit, lambda:2]

--
Gabriel Genellina

From: exarkun on
On 08:45 am, tjreedy(a)udel.edu wrote:
>Tom Machinski wrote:
>>In most cases, `list(generator)` works as expected. Thus,
>>`list(<generator expression>)` is generally equivalent to `[<generator
>>expression>]`.
>>
>>Here's a minimal case where this equivalence breaks, causing a serious
>>and hard-to-detect bug in a program:
>>
>> >>> def sit(): raise StopIteration()
>
>StopIteration is intended to be used only within the .__next__ method
>of iterators. The devs know that other 'off-label' use results in the
>inconsistency you noted, but their and my view is 'don't do that'.

Which is unfortunate, because it's not that hard to get StopIteration
without explicitly raising it yourself and this behavior makes it
difficult to debug such situations.

What's with this view, exactly? Is it just that it's hard to implement
the more desirable behavior?

Jean-Paul
From: Steven D'Aprano on
On Sun, 13 Dec 2009 14:35:21 +0000, exarkun wrote:

>>StopIteration is intended to be used only within the .__next__ method of
>>iterators. The devs know that other 'off-label' use results in the
>>inconsistency you noted, but their and my view is 'don't do that'.
>
> Which is unfortunate, because it's not that hard to get StopIteration
> without explicitly raising it yourself and this behavior makes it
> difficult to debug such situations.

I can't think of any way to get StopIteration without explicitly raising
it yourself. It's not like built-ins or common data structures routinely
raise StopIteration. I don't think I've *ever* seen a StopIteration that
I didn't raise myself.


> What's with this view, exactly? Is it just that it's hard to implement
> the more desirable behavior?

What is that "more desirable behaviour"? That StopIteration is used to
signal that Python should stop iterating except when you want it to be
ignored? Unfortunately, yes, it's quite hard to implement "do what the
caller actually wants, not what he asked for" behaviour -- and even if it
were possible, it goes against the grain of the Zen of Python.

If you've ever had to debug faulty "Do What I Mean" software, you'd see
this as a good thing.



--
Steven
From: exarkun on
On 08:18 pm, steve(a)remove-this-cybersource.com.au wrote:
>On Sun, 13 Dec 2009 14:35:21 +0000, exarkun wrote:
>>>StopIteration is intended to be used only within the .__next__ method
>>>of
>>>iterators. The devs know that other 'off-label' use results in the
>>>inconsistency you noted, but their and my view is 'don't do that'.
>>
>>Which is unfortunate, because it's not that hard to get StopIteration
>>without explicitly raising it yourself and this behavior makes it
>>difficult to debug such situations.
>
>I can't think of any way to get StopIteration without explicitly
>raising
>it yourself. It's not like built-ins or common data structures
>routinely
>raise StopIteration. I don't think I've *ever* seen a StopIteration
>that
>I didn't raise myself.

Call next on an iterator. For example: iter(()).next()
>
>>What's with this view, exactly? Is it just that it's hard to
>>implement
>>the more desirable behavior?
>
>What is that "more desirable behaviour"? That StopIteration is used to
>signal that Python should stop iterating except when you want it to be
>ignored? Unfortunately, yes, it's quite hard to implement "do what the
>caller actually wants, not what he asked for" behaviour -- and even if
>it
>were possible, it goes against the grain of the Zen of Python.
>
>If you've ever had to debug faulty "Do What I Mean" software, you'd see
>this as a good thing.

I have plenty of experience developing and debugging software, Steven.
Your argument is specious, as it presupposes that only two possibilities
exist: the current behavior of some kind of magical faerie land
behavior.

I'm surprised to hear you say that the magical faerie land behavior
isn't desirable either, though. I'd love a tool that did what I wanted,
not what I asked. The only serious argument against this, I think, is
that it is beyond our current ability to create (and so anyone claiming
to be able to do it is probably mistaken).

You chopped out all the sections of this thread which discussed the more
desirable behavior. You can go back and read them in earlier messages
if you need to be reminded. I'm not talking about anything beyond
what's already been raised.

I'm pretty sure I know the answer to my question, though - it's hard to
implement, so it's not implemented.

Jean-Paul