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:
parent
36e7a69be3
commit
a232b370a0
56
pep-0322.txt
56
pep-0322.txt
|
@ -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
|
||||||
=========
|
=========
|
||||||
|
|
Loading…
Reference in New Issue