Updated to reflect comments on comp.lang.python.

This commit is contained in:
Raymond Hettinger 2003-09-25 16:18:28 +00:00
parent c884456428
commit 996d620420
1 changed files with 34 additions and 26 deletions

View File

@ -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