Generating Both Offsets and Items: enumerate

Earlier, we discussed using range to generate the offsets of items in a string, rather than the items at those offsets. In some programs, though, we need both: the item to use, plus an offset as we go. Traditionally, this was coded with a simple for loop that also kept a counter of the current offset:

>>> S = 'spam'
>>> offset = 0
>>> for item in S:
...     print(item, 'appears at offset', offset)
...     offset += 1
...
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

This works, but in recent Python releases a new built-in named enumerate does the job for us:

>>> S = 'spam'
>>> for (offset, item) in enumerate(S):
...     print(item, 'appears at offset', offset)
...
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3

The enumerate function returns a generator object—a kind of object that supports the iteration protocol that we will study in the next chapter and will discuss in more detail in the next part of the book. In short, it has a __next__ method called by the next built-in function, which returns an (index, value) tuple each time through the loop. We can unpack these tuples with tuple assignment in the for loop (much like using zip):

>>> E = enumerate(S)
>>> E
<enumerate object at 0x02765AA8>
>>> next(E)
(0, 's')
>>> next(E)
(1, 'p')
>>> next(E)
(2, 'a')

As usual, we don’t normally see this machinery because iteration contexts—including list comprehensions, the subject of Chapter 14—run the iteration protocol automatically:

>>> [c * i for (i, c) in enumerate(S)]
['', 'p', 'aa', 'mmm']

To fully understand iteration concepts like enumerate, zip, and list comprehensions, we need to move on to the next chapter for a more formal dissection.