Odd closure issue for generators

Scott David Daniels Scott.Daniels at Acm.Org
Fri Jun 5 01:42:07 CEST 2009


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.

--Scott David Daniels
Scott.Daniels at Acm.Org



More information about the Python-list mailing list