lambda generator - curious behavior in 2.5

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Boris Borcic

    #1

    lambda generator - curious behavior in 2.5

    >>x = (lambda : ((yield 666),(yield 777),(yield 888)))()
    >>x.next()
    666
    >>x.next()
    777
    >>x.next()
    888
    >>x.next()
    (None, None, None)
    >>x = (lambda : ((yield 666),(yield 777),(yield 888)) and None)()
    >>x.next()
    666
    >>x.next()
    777
    >>x.next()
    888
    >>x.next()
    Traceback (most recent call last):
    File "<pyshell#2 9>", line 1, in <module>
    x.next()
    StopIteration
    >>>
  • James Stroud

    #2
    Re: lambda generator - curious behavior in 2.5

    Boris Borcic wrote:
    >>x = (lambda : ((yield 666),(yield 777),(yield 888)))()
    >>x.next()
    666
    >>x.next()
    777
    >>x.next()
    888
    >>x.next()
    (None, None, None)
    >>x = (lambda : ((yield 666),(yield 777),(yield 888)) and None)()
    >>x.next()
    666
    >>x.next()
    777
    >>x.next()
    888
    >>x.next()
    >
    Traceback (most recent call last):
    File "<pyshell#2 9>", line 1, in <module>
    x.next()
    StopIteration
    >>>
    Seems like a bug:

    pydef doit():
    ((yield 1), (yield 2), (yield 3))
    ....
    pyx = doit()
    pyx.next()
    1
    pyx.next()
    2
    pyx.next()
    3
    pyx.next()
    ------------------------------------------------------------
    Traceback (most recent call last):
    File "<ipython console>", line 1, in <module>
    <type 'exceptions.Sto pIteration'>

    James

    Comment

    • Gabriel Genellina

      #3
      Re: lambda generator - curious behavior in 2.5

      En Sat, 21 Apr 2007 06:21:00 -0300, Boris Borcic <bborcic@gmail. com>
      escribió:
      >>x = (lambda : ((yield 666),(yield 777),(yield 888)))()
      >>x.next()
      666
      >>x.next()
      777
      >>x.next()
      888
      >>x.next()
      (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:

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

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

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

      Let's check with dis:

      pydis.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


      >>x = (lambda : ((yield 666),(yield 777),(yield 888)) and None)()
      >>x.next()
      666
      >>x.next()
      777
      >>x.next()
      888
      >>x.next()
      >
      Traceback (most recent call last):
      File "<pyshell#2 9>", 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.

      --
      Gabriel Genellina

      Comment

      • Raymond Hettinger

        #4
        Re: lambda generator - curious behavior in 2.5

        [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(resul t);
        result = NULL;
        /* Set exception if not called by gen_iternext() */
        if (arg)
        PyErr_SetNone(P yExc_StopIterat ion);
        }

        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

        Comment

        Working...