lambda generator - curious behavior in 2.5

B

Boris Borcic

x = (lambda : ((yield 666),(yield 777),(yield 888)))()
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
x.next()
StopIteration
 
J

James Stroud

Boris said:
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
x.next()
StopIteration

Seems like a bug:

py> def doit():
((yield 1), (yield 2), (yield 3))
....
py> x = doit()
py> x.next()
1
py> x.next()
2
py> x.next()
3
py> x.next()
------------------------------------------------------------
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
<type 'exceptions.StopIteration'>

James
 
G

Gabriel Genellina

(None, None, None)

I think nobody thought in advance this case (a lambda expression with
yield?), else it would have been forbidden. The lambda is roughly
equivalent to:

def anonymous():
return ((yield 666),(yield 777),(yield 888))
x = anonymous()

but *that* function is forbidden: a generator cannot contain a "return
something" statement. Writing it as a lambda expression, you are bypassing
the compiler check.
Those three None are the 3 yield values, combined into one tuple. You can
verify using send instead of next; the Nones are replaced by the received
values:

py> x = (lambda : ((yield 666),(yield 777),(yield 888)))()
py> x.send(None)
666
py> x.send(1)
777
py> x.send(2)
888
py> x.send(3)
(1, 2, 3)

That was the return in effect. As the function (or lambda) is exited, the
next try should raise StopIteration:

py> x.send(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Let's check with dis:

py> dis.dis(x.gi_frame.f_code)
1 0 LOAD_CONST 0 (666)
3 YIELD_VALUE
4 LOAD_CONST 1 (777)
7 YIELD_VALUE
8 LOAD_CONST 2 (888)
11 YIELD_VALUE
12 BUILD_TUPLE 3
15 RETURN_VALUE


Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
x.next()
StopIteration

This time, the ((tuple) and None) is like saying "discard the tuple and
return None instead", and that fires the usual StopIteration.
 
R

Raymond Hettinger

[Gabriel Genellina]
This time, the ((tuple) and None) is like saying "discard the tuple and
return None instead", and that fires the usual StopIteration.

It looks like the offending code is in the gen_send_ex() function in
Objects/genobject.c:

if (result == Py_None && f->f_stacktop == NULL) {
Py_DECREF(result);
result = NULL;
/* Set exception if not called by gen_iternext() */
if (arg)
PyErr_SetNone(PyExc_StopIteration);
}

The conditional should probably be:

if (result 1= NULL && f->f_stacktop == NULL)

Please open a bug report on SF and assign to me.


Raymond Hettinger
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top