473,395 Members | 1,488 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

StopIteration in the if clause of a generator expression

To confuse a newbies and old hands alike, Bengt Richter wrote:
Need something more straightforward, e.g., a wrapped one-liner:
>>> def guess(n=3): print ("You're right!", 'No more tries for
>>> you!!!')[n-1 in ... (x for x in xrange(n) for t in [raw_input('Guess my name:
')=='Ben']
... if not t or iter([]).next())]
... >>> guess()
To make it a bit clearer, a StopIteration raised in a generator expression
silently terminates that generator:
def stop(): raise StopIteration .... list(i for i in range(10) if i < 5 or stop()) [0, 1, 2, 3, 4]

In a list comprehension, on the other hand, it is propagated:
[i for i in range(10) if i < 5 or stop()]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in stop
StopIteration

Is that an intentional difference?

Peter

Jul 18 '05 #1
16 2255
jfj
Peter Otten wrote:
To confuse a newbies and old hands alike, Bengt Richter wrote:
got me for one:)


To make it a bit clearer, a StopIteration raised in a generator expression
silently terminates that generator:


*any* exception raised from a generator, terminates the generator
jfj
Jul 18 '05 #2
Peter Otten wrote:
To confuse a newbies and old hands alike, Bengt Richter wrote:
Need something more straightforward, e.g., a wrapped one-liner:
>>> def guess(n=3): print ("You're right!", 'No more tries for
>>> you!!!')[n-1 in ... (x for x in xrange(n) for t in [raw_input('Guess my name:
')=='Ben']
... if not t or iter([]).next())]
...
>>> guess()
To make it a bit clearer, a StopIteration raised in a generator

expression silently terminates that generator:
def stop(): raise StopIteration ... list(i for i in range(10) if i < 5 or stop()) [0, 1, 2, 3, 4]

In a list comprehension, on the other hand, it is propagated:
[i for i in range(10) if i < 5 or stop()]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in stop
StopIteration

Is that an intentional difference?

Very interesting. I'm not sure if the designers even considered this
particular subtlety. Why it happens is pretty plain. In the generator
expression case, the generator expression does propogate the
StopIteration, but list() traps it. List comprehensions are internally
treated as a for-loop (kind of), which doesn't trap StopIteration.
Maybe it should.

The list comprehension [ x for x in y ] is currently treated as
equivalent to the following, with byte-code optimizations:

.. _ = []
.. for x in y:
.. _.append(x)
Perhaps it ought to be equivalent to:

.. _ = []
.. try:
.. for x in y:
.. _.append(x)
.. except StopIteration:
.. pass
However, I would guess the Python gods wouldn't approve of this use of
StopIteration, and so would make no sacrifices to get it.
Nevertheless, it seems likely to be how a list comprehension would
behave in Python 3.0, so maybe we should do it.
--
CARL BANKS

Jul 18 '05 #3
[Peter Otten]
a StopIteration raised in a generator expression
silently terminates that generator:
def stop(): raise StopIteration ... list(i for i in range(10) if i < 5 or stop()) [0, 1, 2, 3, 4]

In a list comprehension, on the other hand, it is propagated:
[i for i in range(10) if i < 5 or stop()]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in stop
StopIteration

Is that an intentional difference?


I would call it an unfortunate assymmetry -- one the never comes up unless
you're up to no good ;-)

In a way, both behave identically. They both raise StopIteration. In the case
of the generator expression, that StopIteration is intercepted by the enclosing
list() call. That becomes obvious if you write a pure python equivalent for
list:

def lyst(s):
it = iter(s)
result = []
try:
while 1:
result.append(it.next())
except StopIteration: # guess who trapped StopIter
return result
Raymond Hettinger

Jul 18 '05 #4
On Fri, 01 Apr 2005 16:34:32 GMT, "Raymond Hettinger" <vz******@verizon.net> wrote:
[Peter Otten]
a StopIteration raised in a generator expression
silently terminates that generator:
>>> def stop(): raise StopIteration ...
>>> list(i for i in range(10) if i < 5 or stop())

[0, 1, 2, 3, 4]

In a list comprehension, on the other hand, it is propagated:
>>> [i for i in range(10) if i < 5 or stop()]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in stop
StopIteration

Is that an intentional difference?


I would call it an unfortunate assymmetry -- one the never comes up unless
you're up to no good ;-)

;-)
In a way, both behave identically. They both raise StopIteration. In the case
of the generator expression, that StopIteration is intercepted by the enclosing
list() call. That becomes obvious if you write a pure python equivalent for
list:

def lyst(s):
it = iter(s)
result = []
try:
while 1:
result.append(it.next())
except StopIteration: # guess who trapped StopIter
return result

I assumed that all standard sequence consumers (including list, of course) would intercept
the StopIteration of a sequence given them in the form of a generator expression, so your
lyst example would have an analogue for other sequence consumers as well, right?
I.e., there's not a hidden list(genex) in those others I would hope ;-)

E.g., "in" in my toy exposed more clearly, using Peter's stop:
def show(x): print x,; return x ... def stop(): raise StopIteration ... 2 in (x for x in xrange(5) if show(x)<4 or stop()) 0 1 2
True 7 in (x for x in xrange(5) if show(x)<4 or stop())

0 1 2 3 4
False

BTW I notice that this also nicely shortcuts when the 2 is found.

Regards,
Bengt Richter
Jul 18 '05 #5
> I assumed that all standard sequence consumers (including list, of course)
would intercept
the StopIteration of a sequence given them in the form of a generator expression, so your lyst example would have an analogue for other sequence consumers as well, right? I.e., there's not a hidden list(genex) in those others I would hope ;-)
Right.
E.g., "in" in my toy exposed more clearly, using Peter's stop:
>>> def show(x): print x,; return x ... >>> def stop(): raise StopIteration ... >>> 2 in (x for x in xrange(5) if show(x)<4 or stop()) 0 1 2
True >>> 7 in (x for x in xrange(5) if show(x)<4 or stop())

0 1 2 3 4
False

BTW I notice that this also nicely shortcuts when the 2 is found.


That's a fact.
Raymond
Jul 18 '05 #6
Raymond Hettinger wrote:

(quoting Bengt)
I assumed that all standard sequence consumers (including list, of
course) would intercept the StopIteration of a sequence given them in the
form of a generator expression, so your lyst example would have an
analogue for other sequence consumers as well, right?
I.e., there's not a hidden list(genex) in those others I would hope ;-)
Right.


I see I followed the historical evolvement and saw generator expressions as
a lazy listcomp rather than a cool new way to write a generator. That
turned out to be the road to confusion.

Thanks Carl, thanks Raymond for setting me straight.
I would call it an unfortunate assymmetry -- one the never comes up unless
you're up to no good ;-)


Do you see any chance that list comprehensions will be redefined as an
alternative spelling for list(<generator expression>)?

Peter

Jul 18 '05 #7
jfj wrote:
To make it a bit clearer, a StopIteration raised in a generator
expression silently terminates that generator:


*any* exception raised from a generator, terminates the generator


Yeah, but StopIteration is the only expected exception and therefore the
only one that client code (nearly) always knows to deal with:
def choke(): raise ValueError .... list(i for i in range(10) if i < 3 or choke()) Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in <generator expression>
File "<stdin>", line 1, in choke
ValueError [i for i in range(10) if i < 3 or choke()]

Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 1, in choke
ValueError

Here you can *not* tell apart list(genexp) and listcomp.

(Of course, as has since been pointed out, the StopIteration is actually
caught in the list constructor, so nothing magic to the example in my
initial post)

Peter

Jul 18 '05 #8
[Peter Otten]
Do you see any chance that list comprehensions will be redefined as an
alternative spelling for list(<generator expression>)?


Not likely. It is possible that the latter spelling would make it possible for
Py3.0. eliminate list comps entirely. However, they are very popular and
practical, so my bet is that they will live on.

The more likely change is that in Py3.0 list comps will no longer expose the
loop variable outside the loop.
Raymond Hettinger

Jul 18 '05 #9
Raymond Hettinger wrote:
[Peter Otten]
Do you see any chance that list comprehensions will be redefined as an
alternative spelling for list(<generator expression>)?


Not likely. It is possible that the latter spelling would make it possible for
Py3.0. eliminate list comps entirely. However, they are very popular and
practical, so my bet is that they will live on.


I suspect you're right, but I certainly wouldn't complain if list comps
disappeared. TOOWTDI and all, and I often find myself alternating
between the two when I can't decide which one seems more Pythonic.
(These days I generally write a listcomp, but I wouldn't put any money
on my code being entirely consistent about this...)

STeVe
Jul 18 '05 #10
This is all just making everything far too complicated. What you really
want to do is quite simple:

import itertools
def condition(x): return x < 5

list(itertools.takewhile(condition, (i for i in range(10))))

The 'Stop Iteration In Generator Expression' problem was solved in the
language that List Comprehensions came from, Haskell. Haskell's basic
library, prelude, had a series of functions that have found their way
into the itertools toolbox. I highly recommend having a read of the
itertools docs if you want to continue hacking around with generators.

Regards,
Stephen Thorne

Jul 18 '05 #11
[Steven Bethard]
and I often find myself alternating
between the two when I can't decide which one seems more Pythonic.


Both are pythonic.

Use a genexp when you need a generator
and use a listcomp when you need a list.
Raymond Hettinger
Jul 18 '05 #12
Raymond Hettinger wrote:
[Steven Bethard]
and I often find myself alternating
between the two when I can't decide which one seems more Pythonic.


Both are pythonic.

Use a genexp when you need a generator
and use a listcomp when you need a list.


So do I read this right in preferring
[<x> for <y> in <z>]
over
list(<x> for <y> in <z>)
?

STeVe
Jul 18 '05 #13
> > Use a genexp when you need a generator
and use a listcomp when you need a list.

[Steven Bethard] So do I read this right in preferring
[<x> for <y> in <z>]
over
list(<x> for <y> in <z>)


Yes!
Raymond
Jul 18 '05 #14
On Apr 5, 2005 2:04 AM, Raymond Hettinger <vz******@verizon.net> wrote:
[Steven Bethard]
So do I read this right in preferring
[<x> for <y> in <z>]
over
list(<x> for <y> in <z>)


Yes!


Why? (Serious question. I'm sure that you have a good reason - I just
can't figure out what it is.)

The generator expression has the advantage of not leaking references
into the enclosing namespace. What's advantage of the list comp?

--
Cheers,
Simon B,
si***@brunningonline.net,
http://www.brunningonline.net/simon/blog/
Jul 18 '05 #15
Simon Brunning wrote:
On Apr 5, 2005 2:04 AM, Raymond Hettinger <vz******@verizon.net> wrote:
[Steven Bethard]
> So do I read this right in preferring
> [<x> for <y> in <z>]
> over
> list(<x> for <y> in <z>)


Yes!


Why? (Serious question. I'm sure that you have a good reason - I just
can't figure out what it is.)

The generator expression has the advantage of not leaking references
into the enclosing namespace. What's advantage of the list comp?

The list comprehension is about 15-20% faster according to timeit.py:

C:\Python24\Lib>..\python.exe timeit.py -s "t = range(1000)" "[ x for x in t]"
10000 loops, best of 3: 116 usec per loop

C:\Python24\Lib>..\python.exe timeit.py -s "t = range(1000)" "list(x for x in t)"
1000 loops, best of 3: 144 usec per loop

C:\Python24\Lib>..\python.exe timeit.py -s "t = range(100000)" "[ x for x in t]"
10 loops, best of 3: 13.9 msec per loop

C:\Python24\Lib>..\python.exe timeit.py -s "t = range(100000)" "list(x for x in t)"
10 loops, best of 3: 16.3 msec per loop

Alternatively you could just regard the list comprehension as having
less clutter on the screen so it may be clearer.
Jul 18 '05 #16
> > [Steven Bethard]
So do I read this right in preferring
[<x> for <y> in <z>]
over
list(<x> for <y> in <z>)

[Raymond Hettinger] Yes!

[Simon Brunning] Why? (Serious question. I'm sure that you have a good reason - I just
can't figure out what it is.)

The generator expression has the advantage of not leaking references
into the enclosing namespace. What's advantage of the list comp?


One advantage relates to mental parsing and chunking.
A list comp reads as a single step: "make a list".
The genexp form reads as "make a generator and turn it into a list."

The listcomp form has slightly more economy of expression (it is succinct) and
the brackets are a nice visual cue that may save a neuron or two.

Another advantage is that listcomps are older. They tend to be better
understood already. And, they run on older pythons.

The design rule, "use listcomps to make lists and genexps to make generators",
encourages data centric thinking.With being distracting, it helps maintain an
awareness of whether you're filling memory or
generating elements one-at-a-time.

Internally, there are performance differences favoring listcomps when the
desired output is a list. List comprehensions are interpreted immediately
through syntax rather than a global lookup of the list() builtin. The code for
listcomps does not have to create, switch between, and destroy a separate
stackframe. The compiler creates custom code for list comps that takes
advantage of the new LIST_APPEND opcode.

Partially balancing out all of the above are some small advantages for the
list(somegen) form. It lets you forget about listcomps and it works with other
contructors, deque(somegen) or set(somegen) for example.

To my tastes, the net balance favors using listcomps whenever you need to create
a list.
Raymond Hettinger


Jul 18 '05 #17

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

72
by: Raymond Hettinger | last post by:
Peter Norvig's creative thinking triggered renewed interest in PEP 289. That led to a number of contributors helping to re-work the pep details into a form that has been well received on the...
24
by: Mahesh Padmanabhan | last post by:
Hi, When list comprehension was added to the language, I had a lot of trouble understanding it but now that I am familiar with it, I am not sure how I programmed in Python without it. Now I...
45
by: Joh | last post by:
hello, i'm trying to understand how i could build following consecutive sets from a root one using generator : l = would like to produce : , , , ,
5
by: malcolm | last post by:
Example, suppose you have these 2 tables (NOTE: My example is totally different, but I'm simply trying to setup the a simpler version, so excuse the bad design; not the point here) CarsSold {...
2
by: aj70000 | last post by:
This is my query select ano,max(date),a_subject from MY_TAB where table_name='xyz' and ano=877 group by a_subject,ano order by a_subject ANO max(Date) A_Subject 877 2005-01-20...
9
by: bonono | last post by:
Hi, I initially thought that generator/generator expression is cool(sort of like the lazy evaluation in Haskell) until I notice this side effect. >>>a=(x for x in range(2)) >>>list(a) ...
7
by: Laurent Pointal | last post by:
on win32] Given the following: 45 .... (<generator object at 0x00A79788>,) .... File "<stdin>", line 1 SyntaxError: invalid syntax
5
by: Kris Kowal | last post by:
I had a thought that might be pepworthy. Might we be able to break outer loops using an iter-instance specific StopIteration type? This is the desired, if not desirable, syntax:: import...
6
by: ccy56781 | last post by:
I'm writing to see calcuration process. And so, I can't catch StopIteration... What is mistake? def collatz(n): r= while n>1: r.append(n) n = 3*n+1 if n%2 else n/2
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.