Update PEP based on discussion on comp.lang.python:

* reversed() is being preferred to ireverse() as the best name.

* the sample implementation now shows a check for a custom reverse
  method and a guard against being applied to a mapping.

* added sample output for enumerate.__reversed__ to show an example of
  how a custom reverse method would work

* explained why the function is proposed as a builtin.

* expanded a couple for the real world use cases to show what the
  replacement code would look like.

* improved markup so that the function names get italicized.
This commit is contained in:
Raymond Hettinger 2003-10-30 07:41:20 +00:00
parent 36e7a69be3
commit a232b370a0
1 changed files with 36 additions and 20 deletions

View File

@ -46,35 +46,38 @@ does arise regularly in practice. See `Real World Use Cases`_ below.
Proposal Proposal
======== ========
Add a builtin function called *ireverse()* that makes a reverse Add a builtin function called *reversed()* that makes a reverse
iterator over sequence objects that support __getitem__() and iterator over sequence objects that support __getitem__() and
__len__(). __len__().
The above examples then simplify to:: The above examples then simplify to::
for i in ireverse(xrange(n)): for i in reversed(xrange(n)):
print seqn[i] print seqn[i]
:: ::
for elem in ireverse(seqn): for elem in reversed(seqn):
print elem print elem
The core idea is that the clearest, least error-prone way of specifying The core idea is that the clearest, least error-prone way of specifying
reverse iteration is to specify it in a forward direction and then say reverse iteration is to specify it in a forward direction and then say
*ireverse*. *reversed*.
The implementation could be as simple as:: The implementation could be as simple as::
def ireverse(x): def reversed(x):
try:
return x.__reversed__()
except AttributeError:
pass
if hasattr(x, "has_key"):
raise ValueError("mappings do not support reverse iteration")
i = len(x) i = len(x)
while i > 0: while i > 0:
i -= 1 i -= 1
yield x[i] yield x[i]
If *x* is a mapping, the implementation should return a ValueError with
a message noting that reverse iteration is undefined for mappings.
No language syntax changes are needed. The proposal is fully backwards No language syntax changes are needed. The proposal is fully backwards
compatible. compatible.
@ -82,7 +85,7 @@ compatible.
Alternative Method Names Alternative Method Names
======================== ========================
* *backwards* -- more pithy, less explicit * *ireverse* -- uses the itertools naming convention
* *inreverse* -- no one seems to like this one except me * *inreverse* -- no one seems to like this one except me
The name *reverse* is not a candidate because it duplicates the name The name *reverse* is not a candidate because it duplicates the name
@ -92,15 +95,18 @@ of the list.reverse() which mutates the underlying list.
Custom Reverse Custom Reverse
============== ==============
Objects may optionally provide an *__ireverse__* method that returns Objects may optionally provide a *__reversed__* method that returns
a custom reverse iterator. a custom reverse iterator.
This allows reverse() to be applied to objects that do not have This allows *reverse()* to be applied to objects that do not have
__getitem__() and __len__() but still have some useful way of __getitem__() and __len__() but still have some useful way of
providing reverse iteration. providing reverse iteration.
Using this protocol, enumerate() can be extended to support reversal For example, using this protocol, *enumerate()* can be extended to
whenever the underlying iterator supports it also. support reversal whenever the underlying iterator supports it also::
>>> list(reversed(enumerate("abc")))
[(2, 'c'), (1, 'b'), (0, 'a')]
Real World Use Cases Real World Use Cases
@ -123,6 +129,8 @@ library and comments on why reverse iteration was necessary:
lower-level orderings. A forward version of this algorithm is lower-level orderings. A forward version of this algorithm is
possible; however, that would complicate the rest of the heap code possible; however, that would complicate the rest of the heap code
which iterates over the underlying list in the opposite direction. which iterates over the underlying list in the opposite direction.
The replacement code ``for i in reversed(xrange(n//2))`` makes
clear the range covered and how many iterations it takes.
* mhlib.test() uses:: * mhlib.test() uses::
@ -143,7 +151,7 @@ library and comments on why reverse iteration was necessary:
elements from an ever diminishing pool. In fact, the algorithm can elements from an ever diminishing pool. In fact, the algorithm can
be run in a forward direction but is less intuitive and rarely be run in a forward direction but is less intuitive and rarely
presented that way in literature. The replacement code presented that way in literature. The replacement code
``for i in ireverse(xrange(1, len(x)))`` is much easier ``for i in reversed(xrange(1, len(x)))`` is much easier
to verify visually. to verify visually.
* rfc822.Message.__delitem__() uses:: * rfc822.Message.__delitem__() uses::
@ -160,17 +168,17 @@ Active Alternative
================== ==================
A simpler, but limited alternative is to create a builtin that takes A simpler, but limited alternative is to create a builtin that takes
the same arguments as range() but returns a reverse iterator over the the same arguments as *range()* but returns a reverse iterator over the
range. The idea is that much of the benefit of ireverse() comes range. The idea is that much of the benefit of *reversed()* comes
reducing the intellectual effort it takes to express the arguments for reducing the intellectual effort it takes to express the arguments for
[x]range() when going backwards. A good name is needed for this [x]range() when going backwards. A good name is needed for this
alternative -- revrange() is cleanest so far. alternative -- *revrange()* is cleanest so far.
Rejected Alternative Rejected Alternatives
==================== =====================
Several variants were submitted that attempted to apply ireverse() Several variants were submitted that attempted to apply *reversed()*
to all iterables by running the iterable to completion, saving the to all iterables by running the iterable to completion, saving the
results, and then returning a reverse iterator over the results. results, and then returning a reverse iterator over the results.
While satisfying some notions of full generality, running the input While satisfying some notions of full generality, running the input
@ -178,6 +186,14 @@ to the end is contrary to the purpose of using iterators
in the first place. Also, a small disaster ensues if the underlying in the first place. Also, a small disaster ensues if the underlying
iterator is infinite. iterator is infinite.
Putting the function in another module or attaching it to a type object
is not being considered. Like its cousins, *zip()* and *enumerate()*,
the function needs to be directly accessible in daily programming. Each
solves a basic looping problem: lock-step iteration, loop counting, and
reverse iteration. Requiring some form of dotted access would interfere
with their simplicity, daily utility, and accessibility. They are core
looping constructs, independent of any one application domain.
Copyright Copyright
========= =========