Reputation: 8778
So I can create a reverse iterator on a list:
list(reversed([0,1,2,3]))
[3, 2, 1, 0]
I assume this simply calls getitem
from index len(...)-1
to 0
. But then I cannot also do this:
list(reversed(xrange(4)))
[3, 2, 1, 0]
Now I am a bit confused. Does this create the list from xrange(4)
and then reverse it? If not, how does it know what the last element is and how to go backwards? I read the documentation but it didn't help.
Upvotes: 6
Views: 1337
Reputation: 1121654
reversed()
looks for a __reversed__
special method on the object. List objects provide this, and so does xrange()
:
>>> xrange(4).__reversed__()
<rangeiterator object at 0x106e2fa50>
The iterator object simply produces the values in reverse, no list object is produced.
For objects that do not implement __reversed__
, the reversed()
function uses the length and the __getitem__
method; e.g. reversed()
is essentially equivalent to:
def reversed(seq):
try:
return seq.__reversed__()
except AttributeError:
return (seq[i] for i in xrange(len(seq) - 1, -1 , -1))
where the second part is a generator expression, evaluating lazily. The generator then accesses each item in turn starting at index (length - 1) all the way down to index 0.
Upvotes: 9
Reputation:
Just compare the two:
In [2]: reversed(xrange(4))
Out[2]: <rangeiterator at 0x7fa83291bde0>
In [3]: list(reversed(xrange(4)))
Out[3]: [3, 2, 1, 0]
In [4]: reversed([0,1,2,3])
Out[4]: <listreverseiterator at 0x7fa8328be2d0>
In [5]: list(reversed([0,1,2,3]))
Out[5]: [3, 2, 1, 0]
Upvotes: 1
Reputation: 31260
reversed()
can only take a sequence -- if it took generic iterators, it couldn't know what the final value was without exhausting the iterator and storing all the values.
Luckily, xrange
returns an xrange object that works as a sequence:
>>> x = xrange(10)
>>> len(x)
10
>>> x[9]
9
It also happens to have an actual __reversed__
method, but that's a special case of having all the sequence methods.
Upvotes: 4