I need a callback function that is almost exactly the same for a series of gui events. The function will behave slightly differently depending on which event has called it. Seems like a simple case to me, but I cannot figure out this weird behavior of lambda functions.
So I have the following simplified code below:
def callback(msg): print msg #creating a list of function handles with an iterator funcList=[] for m in ('do', 're', 'mi'): funcList.append(lambda: callback(m)) for f in funcList: f() #create one at a time funcList=[] funcList.append(lambda: callback('do')) funcList.append(lambda: callback('re')) funcList.append(lambda: callback('mi')) for f in funcList: f()
The output of this code is:
mi mi mi do re mi
I expected:
do re mi do re mi
Why has using an iterator messed things up?
I've tried using a deepcopy:
import copy funcList=[] for m in ('do', 're', 'mi'): funcList.append(lambda: callback(copy.deepcopy(m))) for f in funcList: f()
But this has the same problem.
1 Scope of a Lambda Expression. The body of a lambda expression has the same scope as a nested block. The same rules for name conflicts and shadowing apply. It is illegal to declare a parameter or a local variable in the lambda that has the same name as a local variable.
Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.
A lambda function can have any number of parameters, but the function body can only contain one expression. Moreover, a lambda is written in a single line of code and can also be invoked immediately.
To configure a REST API to pass query string parameters to a backend Lambda function, use a Lambda custom integration. To pass query string parameters to an HTTP endpoint, use an HTTP custom integration. Important:Make sure that the input data is supplied as the integration request payload.
When a lambda is created, it doesn't make a copy of the variables in the enclosing scope that it uses. It maintains a reference to the environment so that it can look up the value of the variable later. There is just one m
. It gets assigned to every time through the loop. After the loop, the variable m
has value 'mi'
. So when you actually run the function you created later, it will look up the value of m
in the environment that created it, which will by then have value 'mi'
.
One common and idiomatic solution to this problem is to capture the value of m
at the time that the lambda is created by using it as the default argument of an optional parameter. You usually use a parameter of the same name so you don't have to change the body of the code:
for m in ('do', 're', 'mi'): funcList.append(lambda m=m: callback(m))
The problem here is the m
variable (a reference) being taken from the surrounding scope. Only parameters are held in the lambda scope.
To solve this you have to create another scope for lambda:
def callback(msg): print msg def callback_factory(m): return lambda: callback(m) funcList=[] for m in ('do', 're', 'mi'): funcList.append(callback_factory(m)) for f in funcList: f()
In the example above, lambda also uses the surounding scope to find m
, but this time it's callback_factory
scope which is created once per every callback_factory
call.
Or with functools.partial:
from functools import partial def callback(msg): print msg funcList=[partial(callback, m) for m in ('do', 're', 'mi')] for f in funcList: f()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With