58

Is it possible to return two lists from a list comprehension? Well, this obviously doesn't work, but something like:

rr, tt = [i*10, i*12 for i in xrange(4)]

So rr and tt both are lists with the results from i*10 and i*12 respectively. Many thanks

jamylak
  • 128,818
  • 30
  • 231
  • 230
LarsVegas
  • 6,522
  • 10
  • 43
  • 67

3 Answers3

94
>>> rr,tt = zip(*[(i*10, i*12) for i in xrange(4)])
>>> rr
(0, 10, 20, 30)
>>> tt
(0, 12, 24, 36)
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • 7
    Just running two separate list comprehensions is simpler and probably faster though. – Karl Knechtel May 07 '12 at 09:04
  • Well, I'm looping over ~500.000 rows of a file with more than 50 columns. Are two comprehension in this case also really faster? – LarsVegas May 07 '12 at 09:11
  • I think it would be best to just use a for loop so you don't have to iterate through twice since `zip` requires the arguments to be unpacked and im not sure of a way around that. – jamylak May 07 '12 at 09:31
  • 1
    What i posted above was just a plain example. Actually I was using a for loop to filter some of the entries and then call a function which turned out to be really slow because I call the function item for item. So i figured I'd better pass two lists to the function to speed things up. Well I'm gonna do some testing...Thanks mate! – LarsVegas May 07 '12 at 10:00
  • 1
    You have to give the list comprehension one parameter (which in this case is a tuple that has to be unzipped. To accomplish this you have to use the `*` operator. Have a look [here]:(http://docs.python.org/library/functions.html#zip) – LarsVegas May 07 '12 at 11:27
  • 2
    @thavan the `*` unpacks the list comprehension into arguments for `zip`. You can look up 'list unpacking' for more information. – jamylak May 07 '12 at 11:36
  • 1
    @KarlKnechtel: Asymptotically speaking, one loop will beat two loops any time. – Martijn Pieters Apr 22 '14 at 10:05
  • 1
    This doesn't return two lists, `type(rr)` and `type(tt)` is `tuple`. The OP asks for two lists. – Confounded Aug 19 '19 at 15:05
  • This works great for my case! I had a generator that returns a list of tuples (from multiprocessing) and this is the perfect way to go through the generator only once to grab all the results :D – TYZ Jan 14 '21 at 15:12
28

Creating two comprehensions list is better (at least for long lists). Be aware that, the best voted answer is slower can be even slower than traditional for loops. List comprehensions are faster and clearer.

python -m timeit -n 100 -s 'rr=[];tt = [];' 'for i in range(500000): rr.append(i*10);tt.append(i*12)' 
10 loops, best of 3: 123 msec per loop

> python -m timeit -n 100 'rr,tt = zip(*[(i*10, i*12) for i in range(500000)])' 
10 loops, best of 3: 170 msec per loop

> python -m timeit -n 100 'rr = [i*10 for i in range(500000)]; tt = [i*10 for i in range(500000)]' 
10 loops, best of 3: 68.5 msec per loop

It would be nice to see list comprehensionss supporting the creation of multiple lists at a time.

However,

if you can take an advantage of using a traditional loop (to be precise, intermediate calculations), then it is possible that you will be better of with a loop (or an iterator/generator using yield). Here is an example:

$ python3 -m timeit -n 100 -s 'rr=[];tt=[];' "for i in (range(1000) for x in range(10000)): tmp = list(i); rr.append(min(tmp));tt.append(max(tmp))" 
100 loops, best of 3: 314 msec per loop

$ python3 -m timeit -n 100 "rr=[min(list(i)) for i in (range(1000) for x in range(10000))];tt=[max(list(i)) for i in (range(1000) for x in range(10000))]"
100 loops, best of 3: 413 msec per loop

Of course, the comparison in these cases are unfair; in the example, the code and calculations are not equivalent because in the traditional loop a temporary result is stored (see tmp variable). So, the list comprehension is doing much more internal operations (it calculates the tmp variable twice!, yet it is only 25% slower).

toto_tico
  • 17,977
  • 9
  • 97
  • 116
  • But OP says in a comment "I was using a for loop to filter some of the entries and then call a function which turned out to be really slow because I call the function item for item". I read this as a function with a `return x, y` wanting to keep all the x's in one list and all the y's in another. Doing two comprehensions in this situation would double the time complexity needlessly, since you're throwing away the y's on the first comprehension (which were expensive to compute)...just to recompute them again and throw away the x's this time. In a situation like that I think `zip(*)` is better. – Reedinationer Oct 10 '19 at 19:40
  • @Reedinationer, comprehension lists are still better as long as the inside calculations are exactly the same. I did some tests and I was suprised to see the results. However, then it became obvious: it does not matter what you are doing (because it is the same for all cases), what matters is how fast you are looping. That said, ***if you can take any advantage of using a traditional loop (especifically, the use temporal variable)***, then you might be better of with it (or an `iterator/generator` and the zip`). But then, the differences can be attributed to the inside calculations (code). – toto_tico Oct 11 '19 at 10:19
  • I think your point is still very valid, it is difficult to deduce from that sentence what the OP exactly meant. So, I extended my answer. – toto_tico Oct 11 '19 at 10:44
-3

It is possible for a list comprehension to return multiple lists if the elements are lists. So for example:

>>> x, y = [[] for x in range(2)]
>>> x
[]
>>> y
[]
>>>

The trick with zip function would do the job, but actually is much more simpler and readable if you just collect the results in lists with a loop.

Mariy
  • 5,746
  • 4
  • 40
  • 57
  • 8
    Uh, how is that related to the question? This only works for `range(2)` because only then do you get exactly two lists. And empty lists at that. – Tim Pietzcker May 07 '12 at 09:28
  • The question was: 'is it possible to return two lists from a list comprehension?'. I answer that it is possible, but in my opinion is better to iterate with a loop and collect the results in two separate lists. – Mariy May 07 '12 at 10:00