dorinand
dorinand

Reputation: 1737

What does iter() do to list?

I have this code:

a = ['animal', 'dog', 'car', 'bmw', 'color', 'blue']
a_iter = iter(a)

print(a)
print(a_iter)

print(dict(zip(a,a)))
print(dict(zip(a_iter,a_iter)))

and output is:

['animal', 'dog', 'car', 'bmw', 'color', 'blue']
<list_iterator object at 0x7f2d98b756d8>
{'dog': 'dog', 'car': 'car', 'animal': 'animal', 'color': 'color', 'blue': 'blue', 'bmw': 'bmw'}
{'car': 'bmw', 'color': 'blue', 'animal': 'dog'}

I do not understad, why the zip works differently with a_iter than a. What does iter() do, list is iterable, so why use iter()? Could anybody explain me this with some nice example? I googled about it but I still do not understand it.

Upvotes: 6

Views: 2816

Answers (3)

fferri
fferri

Reputation: 18950

iter(l) returns an iterator object for l. Together with next(i) it can be used for iterating the elements of l.

The code:

for x in l: print(x)

is equivalent to this code explicitly using the iterator:

i = iter(l)
while True:
    try:
        x = next(i)
        print(x)
    except StopIteration:
        break

Note that an iterator can also be traversed with a for loop:

i = iter(l)
for x in i:
    print(x)

zip(a,b) consumes one element from a, b at a time.

When the argument of zip is a sequence, it will create its own iterator for it.

When the argument is an iterator, it will just consume elements from it.

When it is the same iterator in both arguments, each iteration of zip will consume one element of the iterator for the first argument, and one element for the second argument.

>>> a = [1,2,3,4]
>>> b = [10,20,30,40]

>>> list(zip(a, b)) # zip two lists
[(1, 10), (2, 20), (3, 30), (4, 40)]

>>> list(zip(a, a)) # zip a list with itself
[(1, 1), (2, 2), (3, 3), (4, 4)]

>>> i1 = iter(a)
>>> i2 = iter(a)
>>> list(zip(i1, i2)) # same as above, but with iterators
[(1, 1), (2, 2), (3, 3), (4, 4)]

>>> i = iter(a)
>>> list(zip(i, i)) # same as above, but with the same iterator
[(1, 2), (3, 4)]

Upvotes: 5

Martijn Pieters
Martijn Pieters

Reputation: 1125058

iter() does nothing to a list; the list object has an __iter__ method that iter() uses to produce an iterator object. That object has a reference to the original list and an index; every time you ask for the next value in the iterator, the value at the current index is retrieved and returned, and the index is incremented.

You can use the next() function to get the next value from an iterator:

>>> a = ['animal', 'dog', 'car', 'bmw', 'color', 'blue']
>>> a_iter = iter(a)
>>> next(a_iter)  # get the next value
'animal'
>>> next(a_iter)  # get the next value
'dog'

Notice how calling next() again gives you a new value. You can do so until the iterator is done:

>>> three_more = next(a_iter), next(a_iter), next(a_iter)
>>> next(a_iter)  # last one
'blue'
>>> next(a_iter)  # nothing left
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

List iterator objects keep hold of the original list object; changing the list object will reflect in the iterator values produced on next():

>>> b = ['foo', 'bar']
>>> b_iter = iter(b)
>>> next(b_iter)
'foo'
>>> b[1] = 'spam'
>>> b
['foo', 'spam']
>>> next(b_iter)
'spam'

zip() asks for the next value in each of its arguments, which are assumed to be iterables; zip() calls iter() on them all. For iterator objects such as a_iter, iter(a_iter) returns the iterator itself (it's already an iterator, after all):

>>> iter(a_iter)
<list_iterator object at 0x10e7b6a20>
>>> iter(a_iter) is a_iter
True

Since a_iter will yield values from the original list, in order, that means that you get paired-up elements in a dictionary, because zip() has two references to the same object; you effectively creating (next(a_iter), next(a_iter)) as the iterator step values for zip(). If you pass in two references to a, on the other hand, zip() will call iter() twice, creating two separate iterator objects, and each have their own index to track.

Let's look at that in detail. Note that zip() also produces an iterator object, so we can verify that calling next() on zip() in turn causes a_iter to step forward twice:

>>> a_iter = iter(a)
>>> a_iter_zip = zip(a_iter, a_iter)
>>> a_iter_zip   # a zip object is an iterator too
<zip object at 0x10e7ba8c8>
>>> next(a_iter_zip)  # get next value of a_iter, together with the next value of a_iter
('animal', 'dog')
>>> next(a_iter)  # the a-list iterator was advanced, so now we get 'car'
'car'
>>> next(a_iter_zip)  # now a_iter is at bmw, so we get bmw and color
('bmw', 'color')

Iterators are independent objects, they each have their own index:

>>> a_iter1 = iter(a)
>>> a_iter2 = iter(a)   # different iterator from a_iter1
>>> next(a_iter1), next(a_iter1)  # what zip() does
('animal', 'dog')
>>> next(a_iter2), next(a_iter2)  # iter2 is independent
('animal', 'dog')

So when you use zip(a, a), what really happens is that zip() calls iter(a) two times, creating two new iterators, and both are used to create the output:

>>> a_iter1 = iter(a)
>>> a_iter2 = iter(a)
>>> a_iter_1_and_2_zip = zip(a_iter1, a_iter2)
>>> next(a_iter_1_and_2_zip)  # values from a_iter1 and a_iter2
('animal', 'animal')
>>> next(a_iter_1_and_2_zip)  # moving in lockstep
('dog', 'dog')
>>> next(a_iter1)   # moving one of these two one step along, to 'car'
'car'
>>> next(a_iter_1_and_2_zip)   # so a_iter1 is one step ahead!
('bmw', 'car')
>>> next(a_iter1)   # another extra step
'color'
>>> next(a_iter_1_and_2_zip)   # so a_iter1 is two steps ahead!
('blue', 'bmw')

Upvotes: 11

rahul mehra
rahul mehra

Reputation: 438

iter() function return a instance of iterator on which we can iterate for getting all the value one by one.It is memory efficient function because it stores only current element value.

Upvotes: 1

Related Questions