2

I'd like to programmatically create a list of functions, each of which returns a dictionary with a specific key, and always the same value. That is:

l1 = [lambda _: {0: 42}, lambda _: {2: 42}, lambda _: {3: 42}]

I'm trying to achieve the same result programmatically. To try and avoid the usual problem with lazy evaluation, I thought I'd only need to create a generator for generating the functions on the fly:

gen = (lambda _: {f: 42} for f in range(3))

And then use a simple list comprehension to get the list:

l2 = [f for f in gen]

That's not sufficient. In fact, if you compare func_clousures with one another, they're all the same:

x[0].func_closure == x[1].func_closure == x[2].func_closure  # this is True

What's the best way to get the result I'm looking for?

Jir
  • 2,985
  • 8
  • 44
  • 66
  • Maybe I'm misunderstanding but how is `create a list of functions, each of which returns a dictionary with a specific key, and always the same value` any different to a list of dicts? – SuperBiasedMan May 18 '15 at 11:54
  • @SuperBiasedMan: the values are to be evaluated later. – mike3996 May 18 '15 at 11:54
  • This may help http://stackoverflow.com/questions/938429/scope-of-python-lambda-functions-and-their-parameters –  May 18 '15 at 11:56
  • @SuperBiasedMan The main difference is I'm looking for a list of functions (callable objects, really) whereas a list of dicts is not callable. Put it another way, I need to be able to call `l2[0]('something')`. – Jir May 18 '15 at 11:57
  • 1
    Well known Python scoping gotcha. Short answer : `gen = (lambda _, f=f: {f: 42} for f in range(3))`, long answer here http://stackoverflow.com/questions/12423614/local-variables-in-python-nested-functions – bruno desthuilliers May 18 '15 at 11:58
  • @brunodesthuilliers you're the one who nailed it (2 args needed). If you turn your comment as an answer, I'll mark it as the one. – Jir May 18 '15 at 12:04
  • Side note: `[f for f in gen]` is written more directly as `list(gen)`. – Eric O. Lebigot May 18 '15 at 12:10
  • @Jir: I voted to close this question as duplicate (cf the link) so I don't see the point in posting it as a full answer - the question I linked to already explain the whole thing ;) – bruno desthuilliers May 18 '15 at 12:13
  • @brunodesthuilliers: yup, I saw that later :) – Jir May 18 '15 at 12:31

2 Answers2

3

You are assigning the last value of f to all the lambdas:

gen = (lambda _,f=f: {f: 42} for f in range(3))

As @Jon Clements so kindly pointed out the term is lazy binding, which is described in Common_Gotchas_In_Python

Jir
  • 2,985
  • 8
  • 44
  • 66
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
0

First it depends on which python version you are using. Pyhton3 creates lazy generators for most of iterators. So you could do something like:

l2 = list([lambda: {f: 42} for f in range(3)])

or if you your lambda needs an input parameter

l2 = list([lambda f: {f: 42} for f in range(3)])
Arturs Vancans
  • 4,531
  • 14
  • 47
  • 76