PEP: 279 Title: The enumerate() built-in function Version: $Revision$
Last-Modified: $Date$ Author: python\@rcn.com (Raymond Hettinger)
Status: Final Type: Standards Track Content-Type: text/x-rst Created:
30-Jan-2002 Python-Version: 2.3 Post-History:

Abstract
========

This PEP introduces a new built-in function, `enumerate()` to simplify a
commonly used looping idiom. It provides all iterable collections with
the same advantage that `iteritems()` affords to dictionaries -- a
compact, readable, reliable index notation.

Rationale
=========

Python 2.2 introduced the concept of an iterable interface as proposed
in PEP 234 \[3\]\_. The `iter()` factory function was provided as common
calling convention and deep changes were made to use iterators as a
unifying theme throughout Python. The unification came in the form of
establishing a common iterable interface for mappings, sequences, and
file objects.

Generators, as proposed in PEP 255 \[1\]*, were introduced as a means
for making it easier to create iterators, especially ones with complex
internal execution or variable states. The availability of generators
makes it possible to improve on the loop counter ideas in PEP 212
\[2\]*. Those ideas provided a clean syntax for iteration with indices
and values, but did not apply to all iterable objects. Also, that
approach did not have the memory friendly benefit provided by generators
which do not evaluate the entire sequence all at once.

The new proposal is to add a built-in function, `enumerate()` which was
made possible once iterators and generators became available. It
provides all iterables with the same advantage that `iteritems()`
affords to dictionaries -- a compact, readable, reliable index notation.
Like `zip()`, it is expected to become a commonly used looping idiom.

This suggestion is designed to take advantage of the existing
implementation and require little additional effort to incorporate. It
is backwards compatible and requires no new keywords. The proposal will
go into Python 2.3 when generators become final and are not imported
from `__future__`.

BDFL Pronouncements
===================

The new built-in function is ACCEPTED.

Specification for a new built-in
================================

::

    def enumerate(collection):
       'Generates an indexed series:  (0,coll[0]), (1,coll[1]) ...'
       i = 0
       it = iter(collection)
       while 1:
          yield (i, it.next())
          i += 1

Note A: PEP 212 Loop Counter Iteration \[2\]\_ discussed several
proposals for achieving indexing. Some of the proposals only work for
lists unlike the above function which works for any generator, xrange,
sequence, or iterable object. Also, those proposals were presented and
evaluated in the world prior to Python 2.2 which did not include
generators. As a result, the non-generator version in PEP 212 had the
disadvantage of consuming memory with a giant list of tuples. The
generator version presented here is fast and light, works with all
iterables, and allows users to abandon the sequence in mid-stream with
no loss of computation effort.

There are other PEPs which touch on related issues: integer iterators,
integer for-loops, and one for modifying the arguments to `range` and
`xrange`. The `enumerate()` proposal does not preclude the other
proposals and it still meets an important need even if those are adopted
-- the need to count items in any iterable. The other proposals give a
means of producing an index but not the corresponding value. This is
especially problematic if a sequence is given which doesn't support
random access such as a file object, generator, or sequence defined with
`__getitem__`.

Note B: Almost all of the PEP reviewers welcomed the function but were
divided as to whether there should be any built-ins. The main argument
for a separate module was to slow the rate of language inflation. The
main argument for a built-in was that the function is destined to be
part of a core programming style, applicable to any object with an
iterable interface. Just as `zip()` solves the problem of looping over
multiple sequences, the `enumerate()` function solves the loop counter
problem.

If only one built-in is allowed, then `enumerate()` is the most
important general purpose tool, solving the broadest class of problems
while improving program brevity, clarity and reliability.

Note C: Various alternative names were discussed:

=================
=============================================================
`iterindexed()` five syllables is a mouthful `index()` nice verb but
could be confused the `.index()` method `indexed()` widely liked however
adjectives should be avoided `indexer()` noun did not read well in a
for-loop `count()` direct and explicit but often used in other contexts
`itercount()` direct, explicit and hated by more than one person
`iteritems()` conflicts with key:value concept for dictionaries
`itemize()` confusing because `amap.items()` != `list(itemize(amap))`
`enum()` pithy; less clear than enumerate; too similar to enum in other
languages where it has a different meaning =================
=============================================================

All of the names involving 'count' had the further disadvantage of
implying that the count would begin from one instead of zero.

All of the names involving 'index' clashed with usage in database
languages where indexing implies a sorting operation rather than linear
sequencing.

Note D: This function was originally proposed with optional start and
stop arguments. GvR pointed out that the function call
`enumerate(seqn,4,6)` had an alternate, plausible interpretation as a
slice that would return the fourth and fifth elements of the sequence.
To avoid the ambiguity, the optional arguments were dropped even though
it meant losing flexibility as a loop counter. That flexibility was most
important for the common case of counting from one, as in::

    for linenum, line in enumerate(source,1):  print linenum, line

Comments from GvR: `filter` and `map` should die and be subsumed into
list comprehensions, not grow more variants. I'd rather introduce
built-ins that do iterator algebra (e.g. the iterzip that I've often
used as an example).

    I like the idea of having some way to iterate over a sequence
    and its index set in parallel.  It's fine for this to be a
    built-in.

    I don't like the name "indexed"; adjectives do not make good
    function names.  Maybe ``iterindexed()``?

Comments from Ka-Ping Yee: I'm also quite happy with everything you
proposed ... and the extra built-ins (really 'indexed' in particular)
are things I have wanted for a long time.

Comments from Neil Schemenauer: The new built-ins sound okay. Guido may
be concerned with increasing the number of built-ins too much. You might
be better off selling them as part of a module. If you use a module then
you can add lots of useful functions (Haskell has lots of them that we
could steal).

Comments for Magnus Lie Hetland: I think indexed would be a useful and
natural built-in function. I would certainly use it a lot. I like
`indexed()` a lot; +1. I'm quite happy to have it make PEP 281 obsolete.
Adding a separate module for iterator utilities seems like a good idea.

Comments from the Community: The response to the `enumerate()` proposal
has been close to 100% favorable. Almost everyone loves the idea.

Author response: Prior to these comments, four built-ins were proposed.
After the comments, `xmap`, `xfilter` and `xzip` were withdrawn. The one
that remains is vital for the language and is proposed by itself.
`Indexed()` is trivially easy to implement and can be documented in
minutes. More importantly, it is useful in everyday programming which
does not otherwise involve explicit use of generators.

    This proposal originally included another function ``iterzip()``.
    That was subsequently implemented as the ``izip()`` function in
    the ``itertools`` module.

References
==========

.. \[1\] PEP 255 Simple Generators
http://www.python.org/dev/peps/pep-0255/

.. \[2\] PEP 212 Loop Counter Iteration
http://www.python.org/dev/peps/pep-0212/

.. \[3\] PEP 234 Iterators http://www.python.org/dev/peps/pep-0234/

Copyright
=========

This document has been placed in the public domain.

.. Local Variables: mode: indented-text indent-tabs-mode: nil
fill-column: 70 End:
