May 24, 2008
Let me enumerate the times…

Sometimes you have to iterate over a collection and count the steps you’ve taken. For example, let’s write a really simple function that returns the index of the leftmost occurrence of a given character in the given string.

def indexof(str, char):
  i = 0
  for c in str:
    if c == char:
      return i
    i += 1
  return -1

Or, we could write it as:

def indexof(str, char):
  for i in range(len(str)):
    if str[i] == char:
      return i
  return -1

The second one looks a little bit better. Still, I wish we could write the loop so that the for statement (i.e. the first line of the loop) expresses all aspects of the iteration. In other words, if we are both stepping over the string and incrementing a counter, it is best if they happen simultaneously each time we step through the loop.

Turns out it’s not uncommon to need a counter variable while iterating over the elements of some collection. So Python has a builtin function specifically for this purpose: enumerate (new in Python 2.3). At each iteration it returns a 2-tuple of (counter, object). So our indexof becomes:

def indexof(str, char):
  for index, curchar in enumerate(str):
    if curchar == char:
      return index
  return -1

Now we don’t have to clumsily declare a counter variable and increment it manually. Our final implementation also works with generators and unindexable collections that we cannot subscript (i.e. do collection[index]).