diff --git a/pep-0322.txt b/pep-0322.txt index 0e7a3fc85..dbcbe6c0a 100644 --- a/pep-0322.txt +++ b/pep-0322.txt @@ -46,35 +46,38 @@ does arise regularly in practice. See `Real World Use Cases`_ below. 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 __len__(). The above examples then simplify to:: - for i in ireverse(xrange(n)): + for i in reversed(xrange(n)): print seqn[i] :: - for elem in ireverse(seqn): + for elem in reversed(seqn): print elem 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 -*ireverse*. +*reversed*. 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) while i > 0: i -= 1 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 compatible. @@ -82,7 +85,7 @@ compatible. Alternative Method Names ======================== -* *backwards* -- more pithy, less explicit +* *ireverse* -- uses the itertools naming convention * *inreverse* -- no one seems to like this one except me 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 ============== -Objects may optionally provide an *__ireverse__* method that returns +Objects may optionally provide a *__reversed__* method that returns 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 providing reverse iteration. -Using this protocol, enumerate() can be extended to support reversal -whenever the underlying iterator supports it also. +For example, using this protocol, *enumerate()* can be extended to +support reversal whenever the underlying iterator supports it also:: + + >>> list(reversed(enumerate("abc"))) + [(2, 'c'), (1, 'b'), (0, 'a')] 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 possible; however, that would complicate the rest of the heap code 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:: @@ -143,7 +151,7 @@ library and comments on why reverse iteration was necessary: elements from an ever diminishing pool. In fact, the algorithm can be run in a forward direction but is less intuitive and rarely 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. * rfc822.Message.__delitem__() uses:: @@ -160,17 +168,17 @@ Active Alternative ================== A simpler, but limited alternative is to create a builtin that takes -the same arguments as range() but returns a reverse iterator over the -range. The idea is that much of the benefit of ireverse() comes +the same arguments as *range()* but returns a reverse iterator over the +range. The idea is that much of the benefit of *reversed()* comes reducing the intellectual effort it takes to express the arguments for [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 results, and then returning a reverse iterator over the results. 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 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 =========