Updated to reflect comments on comp.lang.python.
This commit is contained in:
parent
c884456428
commit
996d620420
60
pep-0322.txt
60
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
|
||||
|
|
Loading…
Reference in New Issue