Saturday, May 8, 2010

Unpacking "some" list elements in Python

Programming is not only about efficiency or correctness but also about style. Well, it is for me! Now "my" style is really not that great but from time to time I try to improve it. I may want to make my code more readable, more mathematically oriented or even just more elegant.

Basic unpacking

This is why I was searching for an elegant way to extract some data from a list and leave the rest untouched. Python offers the possibility of unpacking data from a list or tuple. In the code below you'll find the basic unpacking use. For this to work you have to know how many elements there are in your list and assign a variable to each of those elements.

list = [1, 2, 3, 4]
a, b, c, d = list

I believe this is a lot better than the code below which has exactly the same effect but does not make use of the unpacking functionality.

list = [1, 2, 3, 4]
a, b, c, d = list[0], list[1], list[2], list[3]

However this does not solve my initial problem of unapacking only part of the list and leaving the rest as is. Below I'll show you how to do just that in Python 3.x and in Python 2.x.

Catch-all unpacking in Python 3.x

With Python 3.0 came the extended iterable unpacking which brought an elegant way to extract needed data into variables and also specify a "catch-all" variable that will take the rest.

Let's suppose we want to work with the first two elements of the list and leave the rest as a list for future use. In versions 3.x you can do that as shown in the two lines of code that follow.

list = [1, 2, 3, 4]
a, b, *rest = list

The variable a and b will point to the first and second elements respectively. While the variable rest will reference the rest of the list. The "catch-all" variable is marked with an asterisk. This is elegant and concise. Exactly what I was looking for.

However life is not always that easy. While Python 3.x brings lots of new and interesting features it is not backward compatible with python 2.x. Using the Django web development platform I am one of those poor souls stuck in version 2. So let's see what we can come up with in this version.

Catch-all unpacking in Python 2.x

The best way (as in more elegant) I found to replicate the snippet above in Python 2.x is to make use of slices. In the code sample that follows I'll show you how I do it when I want to unpack just the head of the list or more elements because the method is slightly different.

list = [1, 2, 3, 4]
(a, b), rest = list[:2], list[2:] # The first and second elements are interesting.
a, rest = list[0], list[1:]       # Only the head is interesting.

I have to admit I am disappointed with what I could come up with. However I can't think of any other way to do this that would be more elegant. the second case is just a normal assignment with the use of a slice in the right part of the assignment. While the first case is trickier and manages to actually use the unpacking functionality it still lacks clarity. Besides the use of slices only works with list and not with all iterable objects. I can't wait for Django to switch to Python 3.x!

I would be very interested to hear how you would implement the small code pattern above.

7 comments:

  1. Thank you for this; a very comprehensive source!

    ReplyDelete
  2. thanks for sharing :) I still have some problems with python u.u
    BTW, I also have a blog and a web directory, would you like to exchange links? let me know on emily.kovacs14@gmail.com

    ReplyDelete
  3. Can you think of any way to do this with a generator/iterable rather than a fixed list? For example, say you wanted to readlines() from a file 3 lines at a time.

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete