PEP: 279
Title: The enumerate() built-in function
Version: $Revision: 1583 $
Last-Modified: $Date: 2003-03-21 11:57:09 -0800 (Fri, 21 Mar 2003) $
Author: Raymond D. Hettinger <python at rcn.com>
Status: Final
Type: Standards Track
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://python.sourceforge.net/peps/pep-0255.html

    [2] PEP 212 Loop Counter Iteration
        http://python.sourceforge.net/peps/pep-0212.html

    [3] PEP 234 Iterators
        http://python.sourceforge.net/peps/pep-0234.html


Copyright

    This document has been placed in the public domain.