6

This question closely relates to How do I run two python loops concurrently?

I'll put it in a clearer manner: I get what the questioner asks in the above link, something like

for i in [1,2,3], j in [3,2,1]:
    print i,j
    cmp(i,j) #do_something(i,j)

But

L1: for i in [1,2,3] and j in [3,2,1]: doesnt work

Q1. but this was amusing what happened here:

    for i in [1,2,3], j in [3,2,1]:
    print i,j


[1, 2, 3] 0
False 0

Q2. How do I make something like L1 work?

Not Multithreading or parallelism really. (It's two concurrent tasks not a loop inside a loop) and then compare the result of the two.

Here the lists were numbers. My case is not numbers:

for i in f_iterate1() and j in f_iterate2():

UPDATE: abarnert below was right, I had j defined somewhere. So now it is:

>>> for i in [1,2,3], j in [3,2,1]:
    print i,j



Traceback (most recent call last):
  File "<pyshell#142>", line 1, in <module>
    for i in [1,2,3], j in [3,2,1]:
NameError: name 'j' is not defined

And I am not looking to zip two iteration functions! But process them simultaneously in a for loop like situation. and the question still remains how can it be achieved in python.

UPDATE #2: Solved for same length lists

>>> def a(num):
    for x in num:
        yield x


>>> n1=[1,2,3,4]
>>> n2=[3,4,5,6]
>>> x1=a(n1)
>>> x2=a(n2)
>>> for i,j in zip(x1,x2):
    print i,j


1 3
2 4
3 5
4 6
>>> 

[Solved]

Q3. What if n3=[3,4,5,6,7,8,78,34] which is greater than both n1,n2. zip wont work here.something like izip_longest? izip_longest works good enough.

Community
  • 1
  • 1
user2290820
  • 2,709
  • 5
  • 34
  • 62
  • After your edit, I don't understand what you're asking at all. Both answers show you how to process them simultaneously. Inside each iteration of the `for` loop, you have one value from each iterator, and you can use them together in the same expression. Exactly what you wanted to do with your "L1". So… what's the further problem you're trying to solve? – abarnert May 14 '13 at 21:01
  • As for the other part of the update… do you not understand why you get a `NameError` here, or why you got your previous output when `j` was defined? – abarnert May 14 '13 at 21:03
  • Can you show what output you would like to result from the given input? We don't understand what you're trying to achieve. – Chris Johnson May 14 '13 at 21:04
  • Yes, `izip_longest()` will work for you when one list is longer. I already addressed that in my answer. :-) – Martijn Pieters May 14 '13 at 21:12
  • @abarnert NameError problem is solved now. Basically, something like :: for x in process_1 and y in process_2: do_something(with x & y). now if processes(generators) increase, iterators would too and do_something(with x,y,...n) – user2290820 May 14 '13 at 21:13
  • @ChrisJohnson Check update #2 – user2290820 May 14 '13 at 21:14

3 Answers3

13

It's hard to understand what you're asking, but I think you just want zip:

for i, j in zip([1,2,3], [3,2,1]):
    print i, j

for i, j in zip(f_iterate1(), f_iterate2()):
    print i, j

And so on…

This doesn't do anything concurrently as the term is normally used, it just does one thing at a time, but that one thing is "iterate over two sequences in lock-step".


Note that this extends in the obvious way to three or more lists:

for i, j, k in zip([1,2,3], [3,2,1], [13, 22, 31]):
    print i, j, k

(If you don't even know how many lists you have, see the comments.)


In case you're wondering what's going on with this:

for i in [1,2,3], j in [3,2,1]:
    print i,j

Try this:

print [1,2,3], j in [3,2,1]

If you've already defined j somewhere, it will print either [1, 2, 3] False or [1, 2, 3] True. Otherwise, you'll get a NameError. That's because you're just creating a tuple of two values, the first being the list [1,2,3], and the second being the result of the expression j in [3,2,1].

So:

j=0
for i in [1,2,3], j in [3,2 1]:
    print i, j

… is equivalent to:

j=0
for i in ([1,2,3], False):
    print i, 0

… which will print:

[1, 2, 3] 0
False 0
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 1
    Might it be worth noting the `*` operator for more than one list? – squiguy May 14 '13 at 20:53
  • @squiguy: You mean `zip(*list_of_lists)`? I think that will just confuse the question. If you know how many values you want to iterate, you know how many lists you want to zip. If you don't know either, you have to write `for tuple_of_values in zip(*list_of_lists)` or something. But maybe it _is_ worth mentioning that `zip` works on ore than 2 values. – abarnert May 14 '13 at 20:55
  • Yes, exactly. It is out of the scope of the exact question but maybe worth noting...could be helpful at another time. – squiguy May 14 '13 at 20:57
  • @squiguy: I upvoted your comment, and put a reference to it in the answer; hopefully that gives it just the right amount of focus? – abarnert May 14 '13 at 20:59
  • @squiguy I think zip(*list_of_lists) solved the problem. I am trying that out with generator if that works as abarnet has suggest with zip. – user2290820 May 14 '13 at 21:04
  • @abarnert adding another update to answer. Yup that works with zip too! thanks! In my case only if both iterations are over lists of same range.otherwise something like izip_longest(foo())has to be worked out. – user2290820 May 14 '13 at 21:05
  • @user2290820: I told you about that already in my answer, but I have the distinct idea noone can see mine at all. :-P – Martijn Pieters May 14 '13 at 21:06
  • @user2290820: If you have a static group of iterables like the two lists in your first example, or the two generators in your second, `zip(*` will not solve anything, it just makes things more complicated. It's only useful when you don't know _how many_ iterables you have, just that you have a sequence of them. – abarnert May 14 '13 at 21:06
  • @user2290820: In the latest version, [revision 2](http://stackoverflow.com/revisions/16552508/2) you have exactly two examples of what you want to do: `for i in [1,2,3], j in [3,2,1]:` and `for i in f_iterate1() and j in f_iterate2():`. Both of those examples have been covered in both my answer and Martijn's since the start. Neither one has any need for `zip(*`. So, waht are you talking about? – abarnert May 14 '13 at 21:11
  • @MartijnPieters Nope I saw and commented just now. Thanks for that and that solves a lot of generic concurrent processing problems for me. case closed – user2290820 May 14 '13 at 21:21
  • @user2290820: This is not what most people mean by the term "concurrent", and if you continue to use common terms idiosyncratically, you're going to confuse yourself, and confuse people who try to answer your questions. – abarnert May 14 '13 at 22:26
6

You want to use the zip() function:

for i, j in zip([1, 2, 3], [3, 2, 1]):
    #

for i, j in zip(f_iterate1(), f_iterate2()):
    #

zip() pairs up the elements of the input lists, letting you process them together.

If your inputs are large or are iterators, use future_builtins.zip(), or, if you don't care about forward compatibility with Python 3, use itertools.izip() instead; these yield pairs on demand instead of creating a whole output list in one go:

from future_builtins import zip

for i, j in zip(f_iterate1(), f_iterate2()):

Your generators fall in this scenario.

Last but not least, if your input lists have different lengths, zip() stops when the shortest list is exhausted. If you want to continue with the longest list instead, use itertools.izip_longest(); it'll use a fill value when the shorter input sequence(s) are exhausted:

>>> for i, j, k in izip_longest(range(3), range(3, 5), range(5, 10), fillvalue=42):
...     print i, j, k
... 
0 3 5
1 4 6
2 42 7
42 42 8
42 42 9

The default for fillvalue is None.


Your attempt:

for i in [1,2,3], j in [3,2,1]:

is really interpreted as:

for i in ([1,2,3], j in [3,2,1]):

where the latter part is interpreted as a tuple with two values, one a list, the other a boolean; after testing j in [3,2,1], is either True or False. You had j defined as 0 from a previous loop experiment and thus 0 in [3, 2, 1] is False.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • @Martjin Yes this is what I was looking for. It worked. And I've added update #2 for this with a sample code meanwhile testing it with izip_longest. – user2290820 May 14 '13 at 21:17
1

For same-length arrays, you can use the index to refer to corresponding locations in respective lists, like so:

a = [1, 2, 3, 4, 5]
b = [2, 4, 6, 8, 10]
for i in range(len(a)):
    print(a[i])
    print(b[i])

This accesses same indices of both lists at the same time.

AppB
  • 121
  • 1
  • 1
  • 5