Jim
Jim

Reputation: 239

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.

Upvotes: 4

Views: 86

Answers (1)

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160557

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.

Upvotes: 4

Related Questions