Tuesday, July 9, 2013

Simultaneously Looping Techniques in Python

Looping over two lists at the same time might be a bit of a problem, and you will have to use either a custom function or one of already existing Python tools. Also, treat "simultaneously" and "at the same time" terms a bit loosely. I don't think it's possible to run two processes/functions exactly at the same time, many other processes run at your computer and influence the timing of your processes/functions. The best we can achieve is to run them closely enough so as to perform what we need.

Introducing zip

Given two lists of same length:

list1 = [1, 2, 3]
list2 = [4, 5, 6]
for i, k in zip(list1, list2):
     print i, k
and the output is:
1    4
2    5
3    6
zip is a build in python function, no need to import anything, you can use it straight away.
So, what if the lists are of different length? Well, depends on what you want to do with those lists. If you want to do something with all the elements in both lists, you will need to use another tool.
list1 = [1, 2, 3]
list2 = [4, 5, 6, 7]
for i, k in zip(list1, list2):
   print i, k
and the output is:
1    4
2    5
3    6
huh..? same as before?! Well, yeah.
So, what happened?
zip takes two lists, and returns a list of tuples as follows:
zip (list1, list2)
and the output is:
[(1,4), (2, 5), (3, 6)]
and then this is what you are looping over. And when zip runs over the end of one list, he washes hands and say "Here you go, buddy. Enjoy".

Introducing itertools

itertools holds a number of useful functions for different kinds of looping techniques. In my case, only two functions of itertools are of interest to me.

IZIP

In [1]: import itertools
In [2]: list1 = [1, 2, 3]
In [3]: list2 = [4, 5, 6, 7]
In [4]: for i, k in itertools.izip(list1, list2):
...:       print i, k
...: 
1 4
2 5
3 6
In [5]: itertools.izip(list1, list2)
Out[5]: <itertools.izip at 0x27fd5a8>
In [6]: itertools.izip(list1, list2).next()
Out[6]: (1, 4)
and this is the difference between zip() and itertools.izip(). Where zip() returns a list of tuples, izip() returns an iterator( itertools, duh..!).

IZIP_LONGEST

 for i, k in itertools.izip_longest(list1, list2, fillvalue='None'):
    print i, k
...: 
1 4
2 5
3 6
None 7
by default fillvalue will use None. You can use what ever other value that suites you better. izip_longest returns an iterator, same as izip.

And What If I Don't Want to Use itertools and My Lists are of Different Length

Here is another way to do it:
l1 = [1, 2, 3]
l2 = [4, 5, 6, 7]
def get_element(l1, i):
   try:
      return l1[i]
   except IndexError:
      return "None"
for i in xrange(max(len(l1), len(l2))):
   print get_element(l1, i), get_element(l2, i)
and the output is:
1 4
2 5
3 6
None 7
Another way of achieving "simultaneous" looping is multi-threading and of that I will talk in another post sometime soon, when I'll get some free time (whatever that is, lol ;)

No comments:

Post a Comment