11

I'm working with python and trying to isolate a problem I had with lambda functions.

From the following code I was expecting to create two lambda functions, each getting a different x, and the output should be

1
2

but the output is

2
2

Why? And how can I make two different functions? Using def?

def main():
    d = {}
    for x in [1,2]:
        d[x] = lambda: print(x)

    d[1]()
    d[2]()

if __name__ == '__main__':
    main()
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
user985366
  • 1,635
  • 3
  • 18
  • 39
  • 1
    Bad use of lamba. It just returns the turn value of print which is None. Print is supposed to be a command and not an expression. – jamylak Jul 30 '12 at 14:57

3 Answers3

26

The body of the lambda in your code references the name x. The value associated with that name is changed on the next iteration of the loop, so when the lambda is called and it resolves the name it obtains the new value.

To achieve the result you expected, bind the value of x in the loop to a parameter of the lambda and then reference that parameter, as shown below:

def main():
    d = {}
    for x in [1,2]:
        d[x] = lambda x=x: print(x)

    d[1]()
    d[2]()


if __name__ == '__main__':
    main()

>>> 
1
2
dsh
  • 12,037
  • 3
  • 33
  • 51
applicative_functor
  • 4,926
  • 2
  • 23
  • 34
  • Python 3k+ only I think... Does not work (for me) in P2.7 but does in P3.2 and P3.3 – dawg Jul 30 '12 at 14:35
  • @drewk: This answer is applicable to any Python since the introduction of `lambda`. – Steven Rumbalski Jul 30 '12 at 14:42
  • 1
    @drewk good point: the difference is that `print` is a statement in Python < 3 and so it isn't allowed in a `lambda`. It is a function in Python 3, and so it is allowed there. – dsh Jul 30 '12 at 14:42
  • @drewk in 2.7 this version works (without print in lambda): http://ideone.com/IHrsk – applicative_functor Jul 30 '12 at 14:44
  • 1
    If you're really worried about `print` in lambda, just use `sys.stdout.write(x+"\n")` – mgilson Jul 30 '12 at 14:45
  • @mgilson `TypeError: unsupported operand type(s) for +: 'int' and 'str'` . you need to convert the integer. This works for me with python 2.7: `sys.stdout.write(str(x)+"\n")` – user985366 May 26 '15 at 22:29
  • 1
    While the answer code is not wrong, I suggest changing the bound variable in the lambda to name it something else. For clarity improvement. I suggest: `d[x] = lambda y=x: print(y)` . (I tried editing the post but editing less than 6 characters is not allowed) – user985366 May 26 '15 at 22:36
  • Thank you so much @applicative_functor and @dsh! This answer helped me solve an issue I've been trying to fix for three days! – Balaji Pooruli Oct 22 '20 at 01:15
8

Looks like work for partial.

from functools import partial 
def main():
    d = {}
    for x in [1,2]:
        d[x] = partial(lambda x: print(x), x=x)

    d[1]()
    d[2]()


if __name__ == '__main__':
    main()
Nikolay Fominyh
  • 8,946
  • 8
  • 66
  • 102
  • 2
    +1. The simple way to do this is to write this as `d[x] = lambda x=x: print(x)`. However, `partial` allows you to lock down the arguments. This might be written better as `d[x] = partial(print, x)`, since `print` is a function. No need to wrap it in a lambda. – Steven Rumbalski Jul 30 '12 at 14:50
  • Your comment is great! I wrote `lambda`, cause question about `lambda` :) – Nikolay Fominyh Jul 30 '12 at 14:56
4

This will fix it. It is because the x is directly bound to the lambda.

def create_lambda(x):
    return lambda : print(x)

def main():
    d = {}
    for x in [1,2]:
        d[x] = create_lambda(x)

    d[1]()
    d[2]()


if __name__ == '__main__':
    main()
ThirdOne
  • 1,227
  • 9
  • 13