Your tests are doing very different things. With S being 1M elements and T being 300:
[x for x in S for y in T if x==y]= 54.875
This option does 300M equality comparisons.
filter(lambda x:x in S,T)= 0.391000032425
This option does 300 linear searches through S.
[val for val in S if val in T]= 12.6089999676
This option does 1M linear searches through T.
list(set(S) & set(T))= 0.125
This option does two set constructions and one set intersection.
The differences in performance between these options is much more related to the algorithms each one is using, rather than any difference between list comprehensions and lambda.
Your tests are doing very different things. With S being 1M elements and T being 300:
[x for x in S for y in T if x==y]= 54.875
This option does 300M equality comparisons.
filter(lambda x:x in S,T)= 0.391000032425
This option does 300 linear searches through S.
[val for val in S if val in T]= 12.6089999676
This option does 1M linear searches through T.
list(set(S) & set(T))= 0.125
This option does two set constructions and one set intersection.
The differences in performance between these options is much more related to the algorithms each one is using, rather than any difference between list comprehensions and lambda.
When I fix your code so that the list comprehension and the call to filter are actually doing the same work things change a whole lot:
import time
S=[x for x in range(1000000)]
T=[y**2 for y in range(300)]
#
#
time1 = time.time()
N=[x for x in T if x in S]
time2 = time.time()
print 'time diff [x for x in T if x in S]=', time2-time1
#print N
#
#
time1 = time.time()
N=filter(lambda x:x in S,T)
time2 = time.time()
print 'time diff filter(lambda x:x in S,T)=', time2-time1
#print N
Then the output is more like:
time diff [x for x in T if x in S]= 0.414485931396
time diff filter(lambda x:x in S,T)= 0.466315984726
So the list comprehension has a time that's generally pretty close to and usually less than the lambda expression.
The reason lambda expressions are being phased out is that many people think they are a lot less readable than list comprehensions. I sort of reluctantly agree.
I'm curious which the two styles of programming would result with more efficient results, whether it's measured by space or speed.
Let's say I wanted to print a list of the lengths of each word in a sentence, which of the following to methods would be more efficient:
print [len(w) for w in "This is a test sentence".split(" ")] print map(lambda w: len(w), "This is a test sentence".split(" "))
Python: list comprehensions vs. lambda - Stack Overflow
python - Why lambda function in list comprehension is slower than in map? - Stack Overflow
python - List comprehension vs. lambda + filter - Stack Overflow - Priceza
Efficiency of Lambda vs List comprehension
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.
This is a somewhat religious issue in Python. Even though Guido considered removing map, filter and reduce from Python 3, there was enough of a backlash that in the end only reduce was moved from built-ins to functools.reduce.
Personally I find list comprehensions easier to read. It is more explicit what is happening from the expression [i for i in list if i.attribute == value] as all the behaviour is on the surface not inside the filter function.
I would not worry too much about the performance difference between the two approaches as it is marginal. I would really only optimise this if it proved to be the bottleneck in your application which is unlikely.
Also since the BDFL wanted filter gone from the language then surely that automatically makes list comprehensions more Pythonic ;-)
When the list is so small there is no significant difference between the two. If the input list can grow large then there is a worse problem: you're iterating over the whole list, while you could stop at the first element. You could accomplish this with a for loop, but if you want to use a comprehension-like statement, here come generator expressions:
# like list comprehensions but with () instead of []
gen = (b for a, b in foo if a == 'b')
my_element = next(gen)
or simply:
my_element = next(b for a, b in foo if a == 'b')
If you want to learn more about generator expressions give a look at PEP 289.
Note that even with generators and iterators you have more than one choice.
# Python 3:
my_element = next(filter(lambda x: x[0] == 'b', foo))
# Python 2:
from itertools import ifilter
my_element = next(ifilter(lambda (x, y): x == 'b', foo))
I personally don't like and don't recommend this because it is much less readable. It turns out that this is actually slower than my first snippet, but more in general using filter() instead of a generator expression might be faster in some special cases.
In any case if you need benchmarking your code, I recommend using the timeit module.
This is slower than DavidE's answer (I timed it), but has the advantage of simplicity:
z = dict(foo)['b']
Of course it assumes that your keys are all hashable, but that's fine if they are strings. If you need to do multiple lookups though this is definitely the way to go (just be sure to only convert to a dict once).
Because in your first code snippet the lambda is created only once and executed 100000 times while on the second it is created and executed in every iteration.
To be honest I am surprised that the difference is not even bigger but your two timings should grow further apart the largest the length of the iterable is.
As a sidenote, notice that even if you change the lambda to a built-in function that does not have to be created first but rather only looked-up, you still get the same trend:
> py -m timeit "list(map(float, range(10000)))"
200 loops, best of 5: 1.78 msec per loop
> py -m timeit "[float(x) for x in range(10000)]"
100 loops, best of 5: 2.35 msec per loop
An improvement can be achieved by binding the function to a variable, but the list(map()) scheme is still faster.
> py -m timeit "r=float;[r(x) for x in range(10000)]"
100 loops, best of 5: 1.93 msec per loop
As to why the binding to a local variable is faster, you can find more information here.
TLDR: A comprehension evaluates its entire expression every time. A map evaluates its expression once, and applies the result every time.
The expression fed to map and comprehensions are treated differently.
The important parts of what map and comprehensions are doing can be translated in the following way:
def map(func: 'callable', iterable):
for item in iterable:
yield func(item)
def comprehension(expr: 'code', iterable):
for item in iterable:
yield eval(expr)
The important difference is that map already receives a function, whereas comprehension receives an expression.
Now, lambda x:x is an expression that evaluates to a function.
>>> co = compile('lambda x: x', '<stackoverflow>', 'eval')
>>> co
<code object <module> at 0x105d1c810, file "<stackoverflow>", line 1>
>>> eval(co)
<function __main__.<lambda>(x)>
Notably, evaluating the expression to get the function is an action that takes time.
Since map and comprehensions expect different things, the lambda is passed to them differently. What the interpreter does can be expanded with explicit eval/compile:
>>> list(map(eval(compile('lambda x: x', '<stackoverflow>', 'eval')), range(10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(comprehension(compile('(lambda x: x)(item)', '<stackoverflow>', 'eval'), range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
The important difference should be clear now:
- For
map, the expression is evaluated once before it is passed intomap. For each item,mapcalls the resulting function. - For
comprehension, the expression is passed in unevaluated. For each item,comprehensionconstructs the function, then calls the resulting function.
It is important to note that map is not always faster than a comprehension. map benefits from creating/looking up a function once. However, function calls are expensive and a comprehension does not have to use a function.
In general, if the expression uses only names local to the comprehension, it is faster.
In [302]: %timeit list(map(lambda i:i,range(100000)))
...: %timeit [i for i in range(100000)]
8.86 ms ± 38.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.49 ms ± 35.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
If the expression itself has to dynamically do an expensive operation, map does not benefit.
In [302]: %timeit list(map(lambda i: (lambda x:x)(i),range(100000)))
...: %timeit [(lambda x:x)(i) for i in range(100000)]
19.7 ms ± 39.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
15.9 ms ± 43.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)