4 Replies - 725 Views - Last Post: 10 August 2016 - 11:33 AM Rate Topic: -----

#1 jon.kiparsky   User is offline

  • Beginner
  • member icon


Reputation: 12350
  • View blog
  • Posts: 20,984
  • Joined: 19-March 11

A question of closure

Posted 10 August 2016 - 09:19 AM

I ran into a problem illustrated by the following minimal example:

In [310]: lambdas = [lambda x: x + i for i in range(10)]

In [311]: [f(10) for f in lambdas]
Out[311]: [19, 19, 19, 19, 19, 19, 19, 19, 19, 19]


Obviously, we have a scoping issue. It might be because the coffee has not yet kicked in, but I'd like to find a way to get the intuitively correct result, which would be

Out[311]: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]


ie, the value of i in the lambda function should be the value at the time the function is defined, not the value

Any thoughts? The original problem is about defining columns in a report dynamically. The lambdas are for the data lookup function which is part of the column definition.
Note that it is not necessary for the functions to be defined in a comprehension, this was just the easiest way to express the problem. For that matter, lambdas are not strictly needed, I could be perfectly happy with a solution in terms of defs and loops.

Is This A Good Question/Topic? 1
  • +

Replies To: A question of closure

#2 sepp2k   User is offline

  • D.I.C Lover
  • member icon

Reputation: 2770
  • View blog
  • Posts: 4,429
  • Joined: 21-June 11

Re: A question of closure

Posted 10 August 2016 - 09:38 AM

You can introduce a new scope by defining a function:

def make_adder(i):
    return lambda x: x+i

lambdas = [make_adder(i) for i in range(10)]


Was This Post Helpful? 1
  • +
  • -

#3 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7507
  • View blog
  • Posts: 15,558
  • Joined: 16-October 07

Re: A question of closure

Posted 10 August 2016 - 09:55 AM

Threw in parens to clarify what you're after.
>>> lambdas = [(lambda x: x + i) for i in range(10)]
>>> [f(2) for f in lambdas]
[11, 11, 11, 11, 11, 11, 11, 11, 11, 11]



Yep, closure fail. Or is it? You're not quite capturing the i in the lambda, so the last i seems possible.

Here's a more explicit capture that worked:
>>> lambdas = [(lambda j: (lambda x: x + j))(i) for i in range(10)]
>>> [f(2) for f in lambdas]
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]



Basically, forced a new scope, as my i was obviously in a shared scope.
Was This Post Helpful? 1
  • +
  • -

#4 jon.kiparsky   User is offline

  • Beginner
  • member icon


Reputation: 12350
  • View blog
  • Posts: 20,984
  • Joined: 19-March 11

Re: A question of closure

Posted 10 August 2016 - 10:38 AM

Nice. I actually ran into a friend at lunch and we kicked it around a little. He suggested this:
In [318]: lambdas = [lambda x, j=i: x + j for i in range(10)]

In [319]: [f(10) for f in lambdas]
Out[319]: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]


(using the named argument turns the index variable into a local)
Was This Post Helpful? 0
  • +
  • -

#5 andrewsw   User is offline

  • no more Mr Potato Head
  • member icon

Reputation: 6957
  • View blog
  • Posts: 28,696
  • Joined: 12-December 12

Re: A question of closure

Posted 10 August 2016 - 11:33 AM

For anyone reading, this page is a good reference for what is happening (although brief):

Common Gotchas

(The book itself sounds interesting, although not out yet.)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1