Strange behaviour with reversed()

Terry Reedy tjreedy at udel.edu
Thu Oct 18 21:11:58 CEST 2007


"Steven D'Aprano" <steve at REMOVE-THIS-cybersource.com.au> wrote in message 
news:13hdochic30oo43 at corp.supernews.com...
|I don't understand how reversed() is operating. I've read the description
| in the docs:
|
| reversed(seq)
| Return a reverse iterator. seq must be an object which supports the
| sequence protocol (the __len__() method and the __getitem__() method with
| integer arguments starting at 0). New in version 2.4.

The input specification strongly suggests that rev.next() successively 
yields seq[len(seq)-1], ..., seq[0]

The main question is when len(seq) is called -- on creation as it is, or 
immediately before the first yield as you appear to expect, and as it would 
be in this generator (which does NOT match the actual implementation):

def rev(seq):
    n = len(seq)
    while n:
         n =-1
         yield seq[n]

If len(seq) were called repeatedly (after the yield, for instance), then 
termination would no longer guaranteed (see below).

I suppose the doc could be augmented with "The iterator is intialized once 
with len(sequence) when it is created, rather than when first used or 
anytime thereafter."  But I wonder whether that would confuse those not 
thinking about corner case nuances.

| and help(reversed) but neither gives any insight to what happens when you
| use reversed() on a sequence, then modify the sequence.

The sequence can potentially be modified between all calls to RL.next, and 
not just before the first as in your examples.

abcs = list('abc')
for a in reversed(abcs):
    print a
    abcs.append(a)

The 'reverse' of a changing sequence, especially one changing in length, is 
a poorly defined concept.

| >>> L = list("abc")
| >>> RL = reversed(L)
| >>> del L
| >>> list(RL)
| ['c', 'b', 'a']
|
| This suggests that reversed() makes a copy of the list:

Nope.  'del L' merely removes the association between 'L' and the list, 
leaving the internal association between RL and the list and hence the list 
itself.  So the above is consistent with storing a reference (and an index 
initialized to len-1).

| >>> L = list("abc")
| >>> RL = reversed(L)
| >>> L.append("d")
| >>> list(RL)
| ['c', 'b', 'a']
|
| This suggests that reversed() uses a reference to the original list:

It suggests that it uses a reference and an index initialized to len-1 when 
reversed is called (rather than when RL.next is first called).

| >>> RL = reversed(L)
| >>> L[0] = 'e'
| >>> list(RL)
| ['d', 'c', 'b', 'e']
|
| And these examples suggests that reversed() is confused, or at least
| confusing:

This is completely consist with iterating down via reference and index.

| >>> RL = reversed(L)
| >>> del L[2]
| >>> list(RL)
| []

In the internal loop of list, RL first tries to return L[2].  But that 
raises an exception, so RL quits

Terry Jan Reedy






More information about the Python-list mailing list