Consider the following two code snippets.
Snippet 1:
l = range(10)
list(l)
m = reversed(l)
list(m)
l = range(-1)
list(l)
list(m)
Snippet 2:
l = range(10)
m = reversed(l)
l = range(-1)
list(l)
list(m)
The only difference between them is that Snippet 2 does not call list(l)
and list(m)
in its first half.
Bizzarely, the final call to list(m)
in Snippet 1 returns
[]
whereas the final call to list(m)
in Snippet 2 returns
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
These are different values!
This is not the behavior I would have expected. Presumably, the earlier calls to list(l)
and list(m)
in Snippet 1 are triggering some kind of memory optimization; is anybody able to explain to me precisely what is going on, please?
>>> l = range(10)
>>> list(l)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> m = reversed(l)
>>> list(m)
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> l = range(-1)
>>> list(l)
[]
>>> list(m)
[]
>>>
>>> l = range(10)
>>> m = reversed(l)
>>> l = range(-1)
>>> list(l)
[]
>>> list(m)
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Thank you.
reversed
returns an iterator which is single-use: it is exhausted after you feed it to list
(which builds a list from the reversed items) the first time.
In consequent runs, it will yield the empty list since m
, the iterator supplied, is exhausted and can't yield any more values:
m = reversed(l)
print(m) # <list_reverseiterator at 0x7fd2b8518fd0>
list(m) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
list(m) # [] (exhausted)
In your second snippet, you haven't called list
on m
as you do in the first, thereby not exhausting it.
You're only calling it once, in the end, and getting the list you see.