3

Suppose 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')).

I tried this code:

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

But the result is not right:

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

If I call the functions directly, like m = {k: print(k) for k in l}, the output is correct (although the dictionary does not store the right values).

Why doesn't it work with the lambdas?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
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
  • 244,495
  • 58
  • 464
  • 504
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,777
  • 1
  • 22
  • 49