Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a lambda function refer to its parameters in python?

I am new in Python. My task was quite simple -- I need a list of functions that I can use to do things in batch. So I toyed it with some examples like

fs = [lambda x: x + i for i in xrange(10)]

Surprisingly, the call of

[f(0) for f in fs]

gave me the result like [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]. It was not what I expected as I'd like the variable i has different values in different functions.

So My question is:

  1. Is the variable i in lambda global or local?

  2. Does python has the same concept like 'closure' in javascript? I mean does each lambda here holds a reference to the i variable or they just hold a copy of the value of i in each?

  3. What should I do if I'd like the output to be [0, 1, .....9] in this case?

like image 900
dolaameng Avatar asked Mar 04 '11 05:03

dolaameng


People also ask

How does lambda function work in Python?

What is a Lambda Function in Python? A lambda function is an anonymous function (i.e., defined without a name) that can take any number of arguments but, unlike normal functions, evaluates and returns only one expression.

How is lambda () function called?

Lambda Function, also referred to as 'Anonymous function' is same as a regular python function but can be defined without a name. While normal functions are defined using the def keyword, anonymous functions are defined using the lambda keyword. However,they are restricted to single line of expression.

What are lambda parameters?

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.

Can lambda take parameters?

A lambda function can have any number of parameters, but the function body can only contain one expression.


2 Answers

It looks a bit messy, but you can get what you want by doing something like this:

>>> fs = [(lambda y: lambda x: x + y)(i) for i in xrange(10)]
>>> [f(0) for f in fs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Normally Python supports the "closure" concept similar to what you're used to in Javascript. However, for this particular case of a lambda expression inside a list comprehension, it seems as though i is only bound once and takes on each value in succession, leaving each returned function to act as though i is 9. The above hack explicitly passes each value of i into a lambda that returns another lambda, using the captured value of y.

like image 198
Greg Hewgill Avatar answered Nov 13 '22 17:11

Greg Hewgill


The problem you are running into here is the distinction between "early binding" and "late binding".

When Python looks up a variable from an outer scope (i in this case) it uses late binding. That means it sees the value of that variable at the time the function is called, rather than the value at the time the function is defined.

So, in your example code, all 10 lambda functions see the final value assigned to the i variable by the looping process: 9.

Greg's answer shows one way to force early binding behaviour (i.e. create an additional closure and call it immediately while still inside the loop).

Another commonly used approach to forcing early binding semantics is the "default argument hack", which binds the variable as a default argument at function definition time:

>>> fs = [(lambda x, _i=i: x + _i) for i in xrange(10)]
>>> [f(0) for f in fs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Either method works. Greg's has the advantage of not messing with the returned function's signature, the default argument hack is faster and is significantly more readable than adding an additional closure level when defining named functions rather than using lambda expressions.

like image 31
ncoghlan Avatar answered Nov 13 '22 17:11

ncoghlan