[Python-ideas] while conditional in list comprehension ??

Steven D'Aprano steve at pearwood.info
Wed Jan 30 01:34:30 CET 2013


On 30/01/13 02:44, Wolfgang Maier wrote:

> list(i for i in range(100) if i<50 or stop())
> Really (!) nice (and 2x as fast as using itertools.takewhile())!

I think you are mistaken about the speed. The itertools iterators are highly
optimized and do all their work in fast C code. If you are seeing takewhile
as slow, you are probably doing something wrong: untrustworthy timing code,
misinterpreting what you are seeing, or some other error.


Here's a comparison done the naive or obvious way. Copy and paste it into an
interactive Python session:


from itertools import takewhile
from timeit import Timer

def stop(): raise StopIteration

setup = 'from __main__ import stop, takewhile'

t1 = Timer('list(i for i in xrange(1000) if i < 50 or stop())', setup)
t2 = Timer('[i for i in takewhile(lambda x: x < 50, xrange(1000))]', setup)

min(t1.repeat(number=100000, repeat=5))
min(t2.repeat(number=100000, repeat=5))


On my computer, t1 is about 1.5 times faster than t2. But this is misleading,
because it's not takewhile that is slow. I am feeding something slow into
takewhile. If I really need to run as fast as possible, I can optimize the
function call inside takewhile:


from operator import lt
from functools import partial

small_enough = partial(lt, 50)
setup2 = 'from __main__ import takewhile, small_enough'

t3 = Timer('[i for i in takewhile(small_enough, xrange(1000))]', setup2)

min(t3.repeat(number=100000, repeat=5))


On my computer, t3 is nearly 13 times faster than t1, and 19 times faster
than t2. Here are the actual times I get, using Python 2.7:


py> min(t1.repeat(number=100000, repeat=5))  # using the StopIteration hack
1.2609241008758545
py> min(t2.repeat(number=100000, repeat=5))  # takewhile and lambda
1.85182785987854
py> min(t3.repeat(number=100000, repeat=5))  # optimized version
0.09847092628479004



-- 
Steven



More information about the Python-ideas mailing list