Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling list() empties my iterable object?

Tags:

python

list

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?

like image 302
Pieter Kockx Avatar asked Jan 11 '16 22:01

Pieter Kockx


People also ask

What makes a list an iterable object?

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.

Is list an iterable?

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.

What is the difference between iterable and list?

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.

Which function returns an object of type Iterable?

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.


2 Answers

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]
like image 175
dimo414 Avatar answered Oct 22 '22 01:10

dimo414


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)

like image 35
Pynchia Avatar answered Oct 22 '22 01:10

Pynchia