Lambda function in lists

Why is the output of the following two list views different, even if the f and lambda functions are the same?

 f = lambda x: x*x [f(x) for x in range(10)] 

as well as

 [lambda x: x*x for x in range(10)] 

Recall that both type(f) and type(lambda x: x*x) return the same type.

+117
python
May 20 '11 at 18:39
source share
6 answers

The first creates one lambda function and calls it ten times.

The second function does not call the function. It creates 10 different lambda functions. He puts everything from the list. To make it equivalent to the first, you need:

 [(lambda x: x*x)(x) for x in range(10)] 

Or better yet:

 [x*x for x in range(10)] 
+214
May 20 '11 at 18:41
source share

This question touches on the very smelly part of the “known” and “obvious” Python syntax - what takes precedence, lambda, or list comprehension.

I don’t think that the goal of the OP was to create a list of squares from 0 to 9. If that were the case, we could give even more solutions:

 squares = [] for x in range(10): squares.append(x*x) 
  • this is a good old way of imperative syntax.

But that is not the point. Is the fact that W (hy) TF is an ambiguous expression, so illogical? And in the end, I have an idiotic case for you, so do not reject my answer too soon (I had it at the interview).

So, OP comprehension returned a list of lambdas:

 [(lambda x: x*x) for x in range(10)] 

This, of course, is just 10 different copies of the squaring function, see:

 >>> [lambda x: x*x for _ in range(3)] [<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>] 

Please note the memory addresses of lambdas - they are all different!

Of course, you may have a more "optimal" (ha ha) version of this expression:

 >>> [lambda x: x*x] * 3 [<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>] 

Cm? 3 times the same lambda.

Note that I used _ as the for variable. This has nothing to do with x in lambda (it's clouded lexically!). Get it?

I omit the discussion of why the syntax priority is not such that all of this meant:

 [lambda x: (x*x for x in range(10))] 

which can be: [[0, 1, 4, ..., 81]] , or [(0, 1, 4, ..., 81)] , or , which I consider the most logical , it will be a list of 1 element - generator , returning values. This is simply not the case; the language does not work like that.

BUT What if ...

What if you don't shadow the for variable and don't use it in your lambda ???

Well then shit happens. Look at this:

 [lambda x: x * i for i in range(4)] 

this means of course:

 [(lambda x: x * i) for i in range(4)] 

But it does not mean:

 [(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)] 

This is just crazy!

Lambdas in the list comprehension are closures throughout the whole scope of this comprehension. lexical closure, so they refer to i through the link, and not to its value in the evaluation!

So this expression:

 [(lambda x: x * i) for i in range(4)] 

roughly equivalent to:

 [(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)] 

I'm sure we could see more here using the Python decompiler (I mean, for example, the dis module), but this is enough for a discussion not related to Python-VM. So much for the interview question.

Now, how to make a list lambda factors that are really multiplied by consecutive integers? So, similar to the accepted answer, we need to break the direct connection with i , wrapping it in another lambda , which is called inside the list comprehension expression:

Before:

 >>> a = [(lambda x: x * i) for i in (1, 2)] >>> a[1](1) 2 >>> a[0](1) 2 

After:

 >>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)] >>> a[1](1) 2 >>> a[0](1) 1 

(I also had an external lambda variable = i , but I decided that this is a clearer solution - I introduced y so that we all can see what kind of witch is).

Edit 2019-08-30:

Following the suggestion of @josoler, which is also present in the answer of @sheridp, the value of the "loop variable" for understanding the list can be "embedded" in the object - the key to access it at the right time. The After section above does this by wrapping it in another lambda and invoking it immediately with the current value of i . Another way (a little easier to read - it does not produce the 'WAT' effect) is to store the value of i inside the partial object, and the "internal" (original) lambda take it as an argument (passed by the provided partial object during the call), that is:

After 2:

 >>> from functools import partial >>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)] >>> a[0](2), a[1](2) (2, 4) 

Great, but there is still a small twist for you! Suppose we don’t want to make it easier to read the code and pass the factor by name (as a partial argument). Let's do the renaming:

After 2.5:

 >>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)] >>> a[0](1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: <lambda>() got multiple values for argument 'coef' 

Wat?

 >>> a[0]() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: <lambda>() missing 1 required positional argument: 'x' 

Wait ... Are we changing the number of arguments to 1 and moving from "too much" to "too little"?

Well, this is not a real WAT when we pass coef to partial this way, it becomes the key argument, so it should follow the positional argument x , like so:

After 3:

 >>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)] >>> a[0](2), a[1](2) (2, 4) 

I would prefer the latest version over the nested lambda, but to each his own ...

+75
Dec 01 '15 at 13:14
source share

The big difference is that the first example does call lambda f(x) , and the second example does not.

Your first example is equivalent to [(lambda x: x*x)(x) for x in range(10)] , and your second example is equivalent to [f for x in range(10)] .

+17
May 20 '11 at 18:41
source share

The first

 f = lambda x: x*x [f(x) for x in range(10)] 

runs f() for each value in the range, so f(x) for each value

second

 [lambda x: x*x for x in range(10)] 

runs a lambda for each value in the list, so it generates all these functions.

+8
May 20 '11 at 18:42
source share

People gave good answers, but forgot to mention the most important part, in my opinion: In the second example, the list comprehension X NOT the same as the lambda function X , they are completely unrelated. So the second example is actually the same as:

 [Lambda X: X*X for I in range(10)] 

Internal iterations on range(10) are only responsible for creating 10 similar lambda functions in the list (10 separate functions, but completely similar), which return the power 2 of each input).

On the other hand, the first example works in a completely different way, since X iterations DO interacts with the results, for each iteration the value is X*X , so the result will be [0,1,4,9,16,25, 36, 49, 64 ,81]

+4
Aug 27 '15 at 15:46
source share

The other answers are correct, but if you are trying to compile a list of functions, each of which has a different parameter that can be executed later, the following code will do this:

 import functools a = [functools.partial(lambda x: x*x, x) for x in range(10)] b = [] for i in a: b.append(i()) In [26]: b Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

While this example is contrived, I found it useful when I need a list of functions, each of which prints something else, i.e.

 import functools a = [functools.partial(lambda x: print(x), x) for x in range(10)] for i in a: i() 
+3
Jan 24 '17 at 14:52
source share



All Articles