Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Generator expressions vs. comprehensions

6 views
Skip to first unread message

Ian Kelly

unread,
May 24, 2010, 6:31:13 PM5/24/10
to pytho...@python.org
Hi all,

I just ran into an interesting but rather subtle little wart today.
The following expressions are not functionally equivalent, even in
Python 3:

tuple(iterator.next() for i in xrange(n))

tuple([iterator.next() for i in xrange(n)])

In the first case, if iterator.next() raises a StopIteration, the
exception is swallowed by the generator expression. The expression
evaluates to a truncated tuple, and the StopIteration is not
propagated.

In the second case, the StopIteration exception is propagated as
expected by the list comprehension. Set and dict comprehensions also
behave this way in Python 3.

Is this distinction generally known? The generator expression
behavior is understandable since a generator would do the same thing,
but I'm disappointed that the inconsistency exists and wasn't fixed in
Python 3 when we had the chance.

Cheers,
Ian

Carl Banks

unread,
May 24, 2010, 6:47:32 PM5/24/10
to


As a general rule you shouldn't call the next() method/function
without arranging to catch StopIteration, unless you know the iterator
will never raise StopIteration (such as it if it itertools.count()).
The problem is that if a StopIteration "leaks", as it does with the
generator examples, another iterator further up the stack might catch
it and exit.

The situation here is known. It can't be corrected, even in Python 3,
without modifying iterator protocol to tie StopIteration to a specific
iterator. This is possible and might be worth it to avoid hard-to-
diagnose bugs but it would complicate iterator protocol, which becomes
less useful as it becomes more complex.


Carl Banks

Antoine Pitrou

unread,
May 24, 2010, 7:50:48 PM5/24/10
to pytho...@python.org
On Mon, 24 May 2010 15:47:32 -0700 (PDT)
Carl Banks <pavlove...@gmail.com> wrote:
> >
> > Is this distinction generally known?

Yes, it is.

> > The generator expression
> > behavior is understandable since a generator would do the same thing,
> > but I'm disappointed that the inconsistency exists and wasn't fixed in
> > Python 3 when we had the chance.

Why don't you use itertools.islice()?

Michele Simionato

unread,
May 25, 2010, 12:12:54 AM5/25/10
to
On May 25, 12:47 am, Carl Banks <pavlovevide...@gmail.com> wrote:
> The situation here is known.  It can't be corrected, even in Python 3,
> without modifying iterator protocol to tie StopIteration to a specific
> iterator.  This is possible and might be worth it to avoid hard-to-
> diagnose bugs but it would complicate iterator protocol, which becomes
> less useful as it becomes more complex.

The situation here is a known and could be corrected by changing the
meaning of list comprehension,
for instance by having [x for x in iterable] to be an alias for list(x
for x in iterable). In such a way the StopIteration exception would be
always swallowed and there would be consistency with generator
expressions (by construction). However, the list comprehension would
become non-equivalent to the corresponding for-loop with an .append,
so somebody would be un happy anyway :-/

Peter Otten

unread,
May 25, 2010, 3:08:56 AM5/25/10
to
Michele Simionato wrote:

But the list comprehension is already non-equivalent to the for loop as the
loop variable isn't leaked anymore. We do have three similar constructs with
subtle differences.

I think not turning the list-comp into syntactic sugar for list(genexp) in
py3 is a missed opportunity.

Peter

Michele Simionato

unread,
May 25, 2010, 3:26:18 AM5/25/10
to
On May 25, 9:08 am, Peter Otten <__pete...@web.de> wrote:
> But the list comprehension is already non-equivalent to the for loop as the
> loop variable isn't leaked anymore. We do have three similar constructs with
> subtle differences.
>
> I think not turning the list-comp into syntactic sugar for list(genexp) in
> py3 is a missed opportunity.

Actually I do agree with the feeling, and this is not the only missed
opportunity in Python 3 :-/

Terry Reedy

unread,
May 25, 2010, 1:09:23 PM5/25/10
to pytho...@python.org
On 5/25/2010 3:08 AM, Peter Otten wrote:
> Michele Simionato wrote:

> I think not turning the list-comp into syntactic sugar for list(genexp) in
> py3 is a missed opportunity.

Implementing it that way was tried but was much slower than the current
implementation. If one uses StopIteration as it is intended to be used
(and is so documented), then, I believe, they are equivalent. There was
a conscious decision to not slow comprehensions for the many to cater to
the very few.

Terry Jan Reedy

Terry Reedy

unread,
May 25, 2010, 1:19:50 PM5/25/10
to pytho...@python.org
On 5/25/2010 1:09 PM, Terry Reedy wrote:
> On 5/25/2010 3:08 AM, Peter Otten wrote:
>> Michele Simionato wrote:
>
>> I think not turning the list-comp into syntactic sugar for
>> list(genexp) in
>> py3 is a missed opportunity.
>
> Implementing it that way was tried but was much slower than the current
> implementation. If one uses StopIteration as it is intended to be used
> (and is so documented), then, I believe, they are equivalent. There was
> a conscious decision to not slow comprehensions for the many to cater to
> the very few.

And those few can always write list(genexp) instead of [genexp] (or
set...) when the minute difference actually matters.

> Terry Jan Reedy
>


0 new messages