looping over more than one list

Discussion in 'Python' started by Tim Chase, Feb 16, 2006.

  1. Tim Chase

    Tim Chase Guest

    > def lowest(s1,s2):
    > s = ""
    > for i in xrange(len(s1)):
    > s += lowerChar(s1,s2)
    > return s
    >
    > this seems unpythonic, compared to something like:
    >
    > def lowest(s1,s2):
    > s = ""
    > for c1,c2 in s1,s2:
    > s += lowerChar(c1,c2)
    > return s


    If I understand correctly, something like

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

    is what you're looking for. It will gracefully stop when it
    reaches the end of the shortest input. (in your indexed
    example above, if s2 is shorter than s1, it will likely
    throw an out-of-bounds exception)

    For your example, I'd use

    def lowest(s1,s2):
    return ''.join([lowerChar(c1,c2) for c1,c2 in zip(s1,s2)])

    which seems to do what you describe (I understand that
    appending to strings is an inefficient operation and is
    better done with a join like this)

    -tkc
     
    Tim Chase, Feb 16, 2006
    #1
    1. Advertising

  2. Tim Chase

    Iain King Guest

    When I loop over one list I use:

    for item in items:
    print item

    but often I want to loop through two lists at once, and I've been doing
    this like I would in any other language - creating an index counter and
    incrementing it.
    For example, (completely arbitrary), I have two strings of the same
    length, and I want to return a string which, at each character
    position, has the letter closest to 'A' from each of the original
    strings:

    def lowest(s1,s2):
    s = ""
    for i in xrange(len(s1)):
    s += lowerChar(s1,s2)
    return s

    this seems unpythonic, compared to something like:

    def lowest(s1,s2):
    s = ""
    for c1,c2 in s1,s2:
    s += lowerChar(c1,c2)
    return s

    this doesn't work - s1,s2 becomes a tuple. Is there a way to do this
    that I'm missing? I don't see it in the docs.

    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?

    Iain
     
    Iain King, Feb 16, 2006
    #2
    1. Advertising

  3. > 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?
    >
    > Iain


    Always look in itertools for stuff like this:

    http://docs.python.org/lib/itertools-functions.html#l2h-1392

    for c1, c2 in itertools.izip(s1, s2):
    ...

    I haven't profiled it, but it makes sense that this would be fast.
     
    Dylan Moreland, Feb 16, 2006
    #3
  4. Tim Chase

    EleSSaR^ Guest

    Iain King si รจ profuso/a a scrivere su comp.lang.python tutte queste
    elucubrazioni:

    [cut]

    I think you should take a look at the zip() function.

    You can use for with it like this:

    for elem1, elem2, elem3 in zip(list1, list2, list3):
    .....



    --
    Alan Franzoni <>
    -
    Togli .xyz dalla mia email per contattarmi.
    Rremove .xyz from my address in order to contact me.
    -
    GPG Key Fingerprint:
    5C77 9DC3 BD5B 3A28 E7BC 921A 0255 42AA FE06 8F3E
     
    EleSSaR^, Feb 16, 2006
    #4
  5. Iain King <> 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
     
    Alex Martelli, Feb 16, 2006
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Chuckk Hubbard
    Replies:
    1
    Views:
    321
    Peter Otten
    Jun 9, 2008
  2. Merciadri Luca
    Replies:
    4
    Views:
    851
  3. PerlFAQ Server
    Replies:
    0
    Views:
    89
    PerlFAQ Server
    Jan 30, 2011
  4. Steven D'Aprano
    Replies:
    0
    Views:
    146
    Steven D'Aprano
    Dec 23, 2013
  5. Replies:
    3
    Views:
    118
    Gary Herron
    Dec 23, 2013
Loading...

Share This Page