Lambda function in list comprehensions
The first one creates a single lambda function and calls it ten times.
The second one doesn't call the function. It creates 10 different lambda functions. It puts all of those in a 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)]
List comprehension with lambda function
This is because of Pythons late binding closures. You can fix the issue by writing:
def multipliers():
return [lambda x, i=i : i * x for i in range(4)]
List comprehension and lambdas in Python
The y
in your lambda refers to the last value that y
had in the scope it came from, i.e., 9.
The easiest way to get the behavior you want is to use a default argument in your lambda:
lambda x, y=y: x/y
This captures the value of y
at the moment the lambda function is defined.
You can also do a "double-lambda", calling a function that returns the lambda you want, passing in the desired value of y
:
(lambda y: lambda x: x/y)(y)
Here, the outer lambda provides a new scope each time you call it.
List comprehension vs. lambda + filter
It is strange how much beauty varies for different people. I find the list comprehension much clearer than filter
+lambda
, but use whichever you find easier.
There are two things that may slow down your use of filter
.
The first is the function call overhead: as soon as you use a Python function (whether created by def
or lambda
) it is likely that filter will be slower than the list comprehension. It almost certainly is not enough to matter, and you shouldn't think much about performance until you've timed your code and found it to be a bottleneck, but the difference will be there.
The other overhead that might apply is that the lambda is being forced to access a scoped variable (value
). That is slower than accessing a local variable and in Python 2.x the list comprehension only accesses local variables. If you are using Python 3.x the list comprehension runs in a separate function so it will also be accessing value
through a closure and this difference won't apply.
The other option to consider is to use a generator instead of a list comprehension:
def filterbyvalue(seq, value):
for el in seq:
if el.attribute==value: yield el
Then in your main code (which is where readability really matters) you've replaced both list comprehension and filter with a hopefully meaningful function name.
List comprehension with lambda
Since list comprehension and lambda do not fit well in this case, here some functional programming base approaches to, hope, make clear the difference (between a comprehension expression):
A reduce
approach: a list is contracted to a "number" via a functional
from math import factorial
from functools import reduce
n = 10
reduce(lambda i, j: i + 1/factorial(j), range(n), 0)
#2.7182815255731922
here with map
: consecutive 1-to-1 correspondences in which a function is applied termwise. Finally, the sum
functional contracts the list into a "number".
Note: it should be read from right to left, apply the factorial, then the reciprocal and sum it.
sum(map(int(-1).__rpow__, map(factorial, range(n))))
#2.7182815255731922
Python list comprehension with lambdas
The problem, which is a classic
"gotcha", is
that the i
referenced in the lambda functions is not looked up until the
lambda function is called. At that time, the value of i
is the last value it
was bound to when the for-loop
ended, i.e. 2
.
If you bind i
to a default value in the definition of the lambda
functions, then each i
becomes a local variable, and its default value is evaluated and bound to the function at the time the lambda is defined rather than called.
Thus, when the lambda is called, i
is now looked up in the local scope, and its default value is used:
In [177]: bases = [lambda x, i=i: x**i for i in range(3)]
In [178]: print([b(5) for b in bases])
[1, 5, 25]
For reference:
- Python scopes and namespaces
Is list comprehension implemented via map and lambda function?
No, list comprehensions are not implemented by map and lambda under the hood, not in CPython and not in Pypy3 either.
CPython (3.9.13 here) compiles the list comprehension into a special code object that outputs a list and calls it as a function:
~ $ echo 'x = [a + 1 for a in [1, 2, 3, 4]]' | python3 -m dis
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x107446f50, file "<stdin>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_CONST 2 ((1, 2, 3, 4))
8 GET_ITER
10 CALL_FUNCTION 1
12 STORE_NAME 0 (x)
14 LOAD_CONST 3 (None)
16 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x107446f50, file "<stdin>", line 1>:
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (a)
8 LOAD_FAST 1 (a)
10 LOAD_CONST 0 (1)
12 BINARY_ADD
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
>> 18 RETURN_VALUE
Whereas the equivalent list(map(lambda: ...))
thing is just function calls:
~ $ echo 'x = list(map(lambda a: a + 1, [1, 2, 3, 4]))' | python3 -m dis
1 0 LOAD_NAME 0 (list)
2 LOAD_NAME 1 (map)
4 LOAD_CONST 0 (<code object <lambda> at 0x102701f50, file "<stdin>", line 1>)
6 LOAD_CONST 1 ('<lambda>')
8 MAKE_FUNCTION 0
10 BUILD_LIST 0
12 LOAD_CONST 2 ((1, 2, 3, 4))
14 LIST_EXTEND 1
16 CALL_FUNCTION 2
18 CALL_FUNCTION 1
20 STORE_NAME 2 (x)
22 LOAD_CONST 3 (None)
24 RETURN_VALUE
Disassembly of <code object <lambda> at 0x102701f50, file "<stdin>", line 1>:
1 0 LOAD_FAST 0 (a)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 RETURN_VALUE
List comprehension instead of lambda in DataFrame.apply()?
What about
G['year'] = ["'{:02d}".format(x % 100) for x in G.year]
?
Related Topics
Deleting Multiple Columns Based on Column Names in Pandas
Difference Between Parsing a Text File in R and Rb Mode
Wordnet Lemmatization and Pos Tagging in Python
How to Disable SQLalchemy Caching
Substitute Multiple Whitespace with Single Whitespace in Python
Django Equivalent for Count and Group By
How to Extract Frequency Associated with Fft Values in Python
Check If String Is in a Pandas Dataframe
Securely Storing Environment Variables in Gae with App.Yaml
Remove Reverse Duplicates from Dataframe
Why Doesn't List Have Safe "Get" Method Like Dictionary
How to Write Tests for the Argparse Portion of a Python Module
Why Do Attribute References Act Like This with Python Inheritance
Python SQLite Parameter Substitution with Wildcards in Like