Jim Jim - 1 month ago 8
Python Question

Python 3 call to "list" has odd side-effects

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.

Answer

reversed returns an iterator which is a one trick pony: 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) # <range_iterator object at 0x7fc496ce6c00>
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.