Search code examples
pythonlistpython-3.xside-effects

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.


Solution

  • 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.