October 09, 2003
Zip it

I'm rather pleased with this c.l.py post of mine. Python never ceases to impress me, to give me that "how cool is that?" feeling.

Andy wanted to know how it worked. Here goes:

The zip() built-in function takes a number of sequences as arguments, and returns a list of tuples, each tuple consisting of the nth element of each of the argument sequences. That makes no sense, does it? Never mind, it's easier to demonstrate than to explain:

>>> zip(['a', 'b', 'c'], [1, 2, 3])
[('a', 1), ('b', 2), ('c', 3)]

There, now that makes sense. ;-)

But the OP had a list of lists, like so:

>>> spam = [['-', '-', '-', '-', '-', '-', '-', 'K', 'S', 'A', 'K'], ['-', '-', '-', '-', 'L', 'Q', 'Q', 'T', 'N', 'S', 'E'], ['T', 'L', 'E', 'E', 'L', 'M', 'K', 'L', 'S', 'P', 'E']]

If you just pass spam to zip(), well, that's only one sequence. It's a sequence containing sequences, yes, but it's just a sequence nevertheless. So you'll get a list of tuples, each containing just one of spam's sub lists:

>>> zip(spam)
[(['-', '-', '-', '-', '-', '-', '-', 'K', 'S', 'A', 'K'],), (['-', '-', '-', '-', 'L', 'Q', 'Q', 'T', 'N', 'S', 'E'],), (['T', 'L', 'E', 'E', 'L', 'M', 'K', 'L', 'S', 'P', 'E'],)]

What we actually want to do is to pass each of spam's sub lists into zip() as a seperate argument. We can do this the brute force way easily enough:

>>> zip(spam[0], spam[1], spam[2])
[('-', '-', 'T'), ('-', '-', 'L'), ('-', '-', 'E'), ('-', '-', 'E'), ('-', 'L', 'L'), ('-', 'Q', 'M'), ('-', 'Q', 'K'), ('K', 'T', 'L'), ('S', 'N', 'S'), ('A', 'S', 'P'), ('K', 'E', 'E')]

This give the OP what he was asking for. But I don't want to give you that. It's ugly, doesn't scale well to lists with many sub-lists, and only works if the sublist has a specific number of sub-lists. And being Python, there's a nice way of doing it.

Python's function argument handling is pretty sophisticated in comparison to other languages that I know. You've always been able to define a function that is able to take any number of arguments. The function receives these argument as a tuple:

>>> def clever(*args):
... print args
>>> clever(1, 2, 3)
(1, 2, 3)

Python 2.0 introduced a new way of calling a function with a tuple of arguments. Just pass in the sequence, prefixed with an asterisk, and each element of the sequence gets passed into your function as a separate positional argument

>>> eggs = [1, 2, 3]
>>> clever(*eggs)
(1, 2, 3)

We can use this feature to pass each element of code>spam to zip() separately, as so:

>>> zip(*spam)
[('-', '-', 'T'), ('-', '-', 'L'), ('-', '-', 'E'), ('-', '-', 'E'), ('-', 'L', 'L'), ('-', 'Q', 'M'), ('-', 'Q', 'K'), ('K', 'T', 'L'), ('S', 'N', 'S'), ('A', 'S', 'P'), ('K', 'E', 'E')]


Posted to Python by Simon Brunning at October 09, 2003 11:54 AM
Post a comment

Email Address:



Remember info?