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