a = range(1, 3)
a = iter(a)
list(a)
a = list(a)
a
evaluates to [ ]
.
a = range(1, 3)
a = iter(a)
a = list(a)
a
evaluates to [1, 2]
.
The first result is unexpected to me. What semantics are going on here?
Iterable is an object which can be looped over or iterated over with the help of a for loop. Objects like lists, tuples, sets, dictionaries, strings, etc. are called iterables. In short and simpler terms, iterable is anything that you can loop over.
A list is an iterable. But it is not an iterator. If we run the __iter__() method on our list, it will return an iterator. An iterator is an object with a state that remembers where it is during iteration.
List : Fully stored in memory, and it will also be an iterator - i.e. you can go from one element to the next. Iterable : Any object which implements the Iterator protocol - i.e. allow you to go from one element to the next. It could use data stored in memory, it could be a file, or each step could be calculated.
iterator] A zero-argument function that returns an object, conforming to the iterator protocol. Whenever an object needs to be iterated (such as at the beginning of a for...of loop), its @@iterator method is called with no arguments, and the returned iterator is used to obtain the values to be iterated.
The issue is not list()
but iter()
which as documented returns a single-use iterator
. Once something has accessed the iterator
's elements, the iterator is permanently empty. The more commonly used iterable type is (normally) reusable, and the two types shouldn't be confused.
Note that you don't need iter()
in order to turn a range
into a list
, because list()
takes an iterable
as an argument:
>>> a = range(1, 3)
>>> list(a)
[1, 2]
>>> list(a)
[1, 2]
And it is only the iterator
returned by iter()
that is single-use:
>>> b = iter(a)
>>> list(b)
[1, 2]
>>> list(b)
[]
>>> list(a)
[1, 2]
Let's examine what happens:
>>> a = range(1, 3)
>>> a is iter(a)
False
as you can see, iter
gives a new iterator object, which is not a
itself
>>>> a = iter(a)
the name a
now corresponds to the distinct iterator object iter
gave us (just as if iter(a)
had returned itself, e.g. as it happens with zip
and with files)
>>> list(a)
[1, 2]
exhausts the iterator, therefore
>>> list(a)
[]
gives nothing as the iterator has been used (iterated on) already
Here are a few more experiments you can try to fully grasp what happens:
>>> a = range(1, 3)
>>> a
range(1, 3)
>>> type(a)
<class 'range'>
>>> b = iter(a)
>>> b
<range_iterator object at 0x7f331a6d96c0>
>>> type(b)
<class 'range_iterator'>
>>> a is b
False
>>> list(b)
[1, 2]
>>> list(b)
[]
>>> list(a)
[1, 2]
>>> list(a)
[1, 2]
>>> a
range(1, 3)
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'range' object is not an iterator
>>> b=iter(a)
>>> next(b)
1
>>> next(b)
2
>>> next(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> z=zip(a,b)
>>> iter(z) is z
True
>>> with open('words.txt') as f:
... iter(f) is f
...
True
Note: on Python 2 quite a few functions return lists instead of iterators (e.g. zip
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With