Odd closure issue for generators

David Stanek dstanek at dstanek.com
Mon Jun 8 12:57:24 EDT 2009


On Thu, Jun 4, 2009 at 7:42 PM, Scott David
Daniels<Scott.Daniels at acm.org> wrote:
> Brian Quinlan wrote:
>>
>> This is from Python built from the py3k branch:
>>  >>> c = (lambda : i for i in range(11, 16))
>>  >>> for q in c:
>> ...     print(q())
>> ...
>> 11
>> 12
>> 13
>> 14
>> 15
>>  >>> # This is expected
>>  >>> c = (lambda : i for i in range(11, 16))
>>  >>> d = list(c)
>>  >>> for q in d:
>> ...     print(q())
>> ...
>> 15
>> 15
>> 15
>> 15
>> 15
>>  >>> # I was very surprised
>
> You are entitled to be surprised.  Then figure out what is going on.
> Hint: it is the moral equivalent of what is happening here:
>
>    >>> c = []
>    >>> for i in range(11, 16):
>            c.append(lambda: i)
>
>    >>> i = 'Surprise!'
>    >>> print([f() for f in c])
>    ['Surprise!', 'Surprise!', 'Surprise!', 'Surprise!', 'Surprise!']
>
>    >>> i = 0
>    >>> print([f() for f in c])
>    [0, 0, 0, 0, 0]
>
> The body of your lambda is an un-evaluated expression with a reference,
> not an expression evaluated at the time of loading c.  TO get what you
> expected, try this:
>
>    >>> c = []
>    >>> for i in range(11, 16):
>            c.append(lambda i=i: i)
>
>    >>> i = 'Surprise!'
>    >>> print([f() for f in c])
>    [11, 12, 13, 14, 15]
>
> When you evaluate a lambda expression, the default args are evaluated,
> but the expression inside the lambda body is not.  When you apply that
> evaluated lambda expression, the expression inside the lambda body is
> is evaluated and returned.
>

Getting around this can be pretty easy:

  c = (lambda i=i: i for i in range(11, 16))
  for q in (list(c)):
      print(q())


-- 
David
blog: http://www.traceback.org
twitter: http://twitter.com/dstanek



More information about the Python-list mailing list