Parametrized lambda not working as expected

Question:

I wanted to make multiple variants of a function with some different default parameters, but it didn’t work good at all.

Here’s a simplified example:

In [1]: def showme(what):
   ...:     print("My number is %d!" % what)
   ...:     

In [2]: showme(99)
My number is 99!

In [3]: fns = []

In [4]: for i in range(0, 10):
    fns.append( lambda: showme(2**i) )     

In [5]: for fn in fns:
    fn()

My number is 512!
My number is 512!
My number is 512!
My number is 512!
My number is 512!
My number is 512!
My number is 512!
My number is 512!
My number is 512!
My number is 512!

In [6]: 

What am I doing wrong, and how can I fix it? Obviously I expected 1,2,4,8....

In the actual code, the lambda also gets some other arguments, not just i.

When I used hard-coded numbers, it worked like charm.

Asked By: MightyPork

||

Answers:

The expression inside a lambda function is evaluated only when the function is called. This means that the i in your lambda will refer to the last value of i in the loop, which is 512.

To fix this problem, you can simply capture the value of i during each iteration like so:

fns.append(lambda i=i: showme(2**i))

Demo:

>>> def showme(what):
...     print("My number is %d!" % what)
...
>>> fns = []
>>> for i in range(0, 10):
...     fns.append(lambda i=i: showme(2**i))
...
>>> for fn in fns:
...     fn()
...
My number is 1!
My number is 2!
My number is 4!
My number is 8!
My number is 16!
My number is 32!
My number is 64!
My number is 128!
My number is 256!
My number is 512!
>>>

This works because default argument values are evaluated as soon as a lambda function is defined.

Answered By: user2555451
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.