3

Possible Duplicate:
What’s going on with the lambda expression in this python function?
What do (lambda) function closures capture in Python?

For example, I have a list of string ['a', 'b', 'c'].

I want to generate one dictionary with the element in that list ('a', 'b' and 'c') as key, and a function which prints the key as the value (print('a'), print('b'), print('c'))

  from __future__ import print_function
  l = ['a', 'b', 'c']
  m = {k: lambda : print(k) for k in l}

I wrote the above code. But the result is not right

  m['a']()
  c
  m['b']()
  c

I know that if I don't use lambda there

m = {k:print(k) for k in l}

This is no problem. But i'd like to know why lambda does not work here.

Any idea how to solve this? Thanks

Community
  • 1
  • 1
WenbinWu
  • 111
  • 4

2 Answers2

3

The reason you're getting c as the output for each key is because the c comes last in the iteration, so the final value of x is c here, and your program can be expanded as follows:

    for x in lis:
        def func():
            print(x)
        dic[x]=func

Here your function is actually accessing the value of a global variable x, not a local variable. And as at the end of iteration x is c, you get c as answer.

Therefore you need to use default arguments to store the value in a local variable.

correct code:

    In [2]: lis=['a', 'b', 'c']

    In [3]: dic={x:lambda i=x:print(i) for x in lis}

    In [4]: dic['a']
    Out[4]: <function __main__.<lambda>>

    In [5]: dic['a']()
    a
    In [6]: dic['b']()
    b

or:

In [16]: for x in lis:
    def func(i=x):
        print(i)    
    dic[x]=func
   ....:     

In [17]: dic['b']()
b
Ashwini Chaudhary
  • 232,417
  • 55
  • 437
  • 487
2

The basic problem you encounter has to do with the binding of the labda expression and you can find several questions in that direction (e.g. What is "lambda binding" in Python? or Python lambda's binding to local values)

A common solution is to use functools.partial to avoid the problem:

from __future__ import print_function
import functools

l = ['a', 'b', 'c']
m = {k: functools.partial(print, k) for k in l}
Community
  • 1
  • 1
Michael Mauderer
  • 3,519
  • 1
  • 22
  • 48