looping over more than one list

Alex Martelli aleaxit at yahoo.com
Thu Feb 16 10:40:51 EST 2006


Iain King <iainking at gmail.com> wrote:
   ...
> This works:
> 
> def lowest(s1,s2):
>     s = ""
>     for c1,c2 in [x for x in zip(s1,s2)]:
>         s += lowerChar(c1,c2)
>     return s
> 
> but it's hardly any more elegant than using a loop counter, and I'm
> guessing it's performance is a lot worse - I assume that the zip
> operation is extra work?

1. [x for x in whateverlist]   just doesn't make sense!  If you need a
copy of whateverlist, just use list(whateverlist).  When you don't need
a copy, like here, just use whateverlist.  I.e., loop this way:

  for c1, c2 in zip(s1, s2):

2. performance is destroyed by building up a big list with a += of many
small pieces.  Use cStringIO (some people prefer it for style reasons)
or (what most people do) ''.join a list where you've accumulated the
results.

3. this case is ideal for build-in function map.

The body of the function could be just one statement:

  return ''.join(map(lowerChar, s1, s2))

This probably gives best performance.

Also consider a genexp and avoiding the unpacking/repacking a la:

  return ''.join(lowerChar(*cs) for cs in zip(s1, s2))

or better:

import itertools

and then

  return ''.join(lowerChar(*cs) for cs in itertools.izip(s1, s2))

or

  return ''.join(itertools.imap(lowerChar, s1, s2))

If you care about performance, use module timeit from the standard
library to measure cases of interest.

If you don't (not especially) then the first solution looks neat,
elegant, readable, and concise.

But never build up substantial strings with a loop of += of small
strings: that's O(N squared) and will MAKE you care about performance
even where you otherwise wouldn't!-)


Alex



More information about the Python-list mailing list