From 996d6204200cd0a3ca40ff8bc10a2d7674f72fe7 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 25 Sep 2003 16:18:28 +0000 Subject: [PATCH] Updated to reflect comments on comp.lang.python. --- pep-0322.txt | 60 +++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/pep-0322.txt b/pep-0322.txt index 71fe5f493..b6bfbc2c2 100644 --- a/pep-0322.txt +++ b/pep-0322.txt @@ -29,16 +29,15 @@ error prone, unnatural, and not especially readable:: One other current approach involves reversing a list before iterating over it. That technique wastes computer cycles, memory, and lines of -code. Also, it only works with lists (strings, for example, do not -define a reverse method):: +code:: rseqn = list(seqn) rseqn.reverse() for value in rseqn: print value -Extending slicing minimizes the code overhead but does nothing for -memory efficiency, beauty, or clarity. +Extending slicing is a third approach that minimizes the code overhead +but does nothing for memory efficiency, beauty, or clarity. Reverse iteration is much less common than forward iteration, but it does arise regularly in practice. See `Real World Use Cases`_ below. @@ -109,6 +108,9 @@ library and comments on why reverse iteration was necessary: . . . del _exithandlers + Note, if the order of deletion is important, then the first form + is still needed. + * difflib.get_close_matches() uses:: result.sort() # Retain only the best n. @@ -149,18 +151,15 @@ library and comments on why reverse iteration was necessary: * platform._dist_try_harder() uses ``for n in range(len(verfiles)-1,-1,-1)`` because the loop deletes selected elements from *verfiles* but needs to leave the rest of - the list intact for further iteration. This use case could be - addressed with *itertools.ifilter()* but would require the - selection predicate to be in a *lambda* expression. The net - result is less clear and readable than the original. A better - reformulation is to replace the first line with the proposed - method. + the list intact for further iteration. * random.shuffle() uses ``for i in xrange(len(x)-1, 0, -1)`` because the algorithm is most easily understood as randomly selecting 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. + presented that way in literature. The replacement code + ``for i in xrange(1, len(x)).iter_backwards()`` is much easier + to mentally verify. * rfc822.Message.__delitem__() uses:: @@ -172,23 +171,32 @@ library and comments on why reverse iteration was necessary: underlying list is altered during iteration. -Rejected Alternative Ideas -========================== +Alternative Ideas +================= -* Add a builtin function, *reverse()* which calls a magic method, - __riter__. I see this as more overhead for no additional benefit. +* Add a builtin function, *riter()* which calls a magic method, + *__riter__*. I see this as more overhead for no additional benefit. -* Add a builtin function, *reverse()* which does the above, and - if *__riter__* is not found, constructs its own using - *__getitem__*, and if *__getitem__* is not found, builds a list - from *__iter__* and returns a reverse iterator over the new list. - The advantage is that one function takes care of almost everything - that is potentially reversible. A disadvantage is that it can - invisibility slip in to a low performance mode (in terms of time - and memory) which would be more visible with an explicit - ``list(obj).reverse()``. Another problem is that *__getitem__* - is also used in mappings as well as sequences and that could lead - to bizarre results. +* Several variants were submitted that provided fallback behavior + when *__riter__* is not defined: + + - fallback to: ``for i in xrange(len(obj)-1,-1,-1): yield obj[i]`` + - fallback to: ``for i in itertools.count(): yield[obj[-i]]`` + - fallback to: ``tmp=list(obj); tmp.reverse(); return iter(tmp)`` + + All of these attempt to save implementing some object methods at the + expense of adding a new builtin function and of creating a new magic + method name. + + The approaches using *__getitem__()* are slower than using a custom + method for each object. Also, the *__getitem__()* variants produce + bizarre results when applied to mappings. + + All of the variants crash when applied to an infinite iterator. + + The last variant can invisibly slip into a low performance mode + (in terms of time and memory) which could be made more visible with + an explicit ``list(obj).reverse()``. Copyright