228 lines
8.5 KiB
Plaintext
228 lines
8.5 KiB
Plaintext
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:
|
|
|
|
|