Python Iterators, Iterable

Iterator

  • An iterator is an object that contains a countable number of values and it can be iterated.
  • Iterators have __next__ () method, which returns the next item of the object.
  • Iterators are implemented as classes.
  • Iterator object must implement two special methods, __iter__() and __next__() collectively called the iterator protocol

Iterable

  • An iterable is any object,that can return an iterator
  • Strings, Lists, tuples, dictionaries, and sets are all iterable objects and these objects have a iter() method which is used to get an iterator

myList is an iterable (a List) whereas myiterator is an iterator.

>>> myList = ["one","Two","Three"]
>>> myiterator = iter(myList)
>>> type(myList) 
<class 'list'>
>>> type(myiterator)
<class 'list_iterator'>

Using for loop to iterate through an iterable object. The for loop actually creates an iterator object and executes the next() method for each loop.

myList = ["Sunday","Monday","Tuesday"]
for x in myList:
    print(x)
Sunday
Monday
Tuesday

myTuple = ("Tulasi","Neem","Raavi","Marri","Jammi")
for x in myTuple:
    print(x)
Tulasi
Neem
Raavi
Marri
Jammi

myNumList = [2,4,6,8,10]
for x in myNumList:
    print(x)
2
4
6
8
10

myName = "Girish"
for x in myName:
    print(x)
G
i
r
i
s
h 

Return an iterator from a String, and print values for every next() call til the end of the String.

>>> myName = "Girish"
>>> myiterator = iter(myName)
>>> print(next(myiterator))
G
>>> print(next(myiterator))
i
>>> print(next(myiterator))
r
>>> print(next(myiterator))
i
>>> print(next(myiterator))
s
>>> print(next(myiterator))
h
>>> print(next(myiterator))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Return an iterator from a List, and print value for every next() call till the end of the List.

>>> myList = ["One","Two","Three"]
>>> myiterator = iter(myList)
>>> print(next(myiterator))
One
>>> print(next(myiterator))
Two
>>> print(next(myiterator))
Three
>>> print(next(myiterator))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

List is iterable but not an iterator

>>> myList = [1,2,3]
>>> next(myList)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

>>> myiterator = iter(myList)
>>> next(myiterator)
1
>>> next(myiterator)
2
>>> next(myiterator)
3
>>> next(myiterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

int, float, bool are not iterable

>>> 8 in [4,8,10]
True
>>> 10 in 12
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: argument of type 'int' is not iterable
>>> 10.4 in 12
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: argument of type 'int' is not iterable
>>> 10 in 12.4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: argument of type 'float' is not iterable
>>> True in False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: argument of type 'bool' is not iterable

Creating an Iterator

To create an object/class as an iterator you have to implement the methods __iter__() and __next__() to your object. The __iter__() method is what makes an object iterable. Behind the scenes, the iter function calls __iter__() method on the given object. The return value of __iter__() is an iterator. It should have a next method and raise StopIteration when there are no more elements.

class OddNumberGen:
    def __init__(self, max):
        self.num = 1
        self.max = max
    def __iter__(self):
        return self
    def __next__(self):
        if self.num <= self.max:
            num = self.num
            self.num += 2
            return num
        else:
            raise StopIteration

myclass = OddNumberGen(20)
myiteration = iter(myclass)
for x in myiteration:
    print(x)
1
3
5
7
9
11
13
15
17
19

The built-in function iter() takes an iterable object and returns an iterator. Each time we call the next method on the iterator gives us the next element. If there are no more elements, it raises a StopIteration.

>>> x = iter([8,9,10])
>>> x
<list_iterator object at 0x00EA4690>
>>> next(x)
8
>>> next(x)
9
>>> next(x)
10
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

All of the itertools functions return iterators.

Make an iterator that returns evenly spaced values starting with n

>>> from itertools import count
>>> counter = count(start = 10)
>>> next(counter)
10
>>> next(counter)
11
>>> next(counter)
12

Make an iterator returning elements from the iterable and saving a copy of each.  Repeats indefinitely. 

>>> from itertools import cycle
>>> cities = cycle(["Bangalore","Hyderabad","Mumbai"])
>>> next(cities)
'Bangalore'
>>> next(cities)
'Hyderabad'
>>> next(cities)
'Mumbai'
>>> next(cities)
'Bangalore'
>>> next(cities)
'Hyderabad'
>>> next(cities)
'Mumbai'
>>> next(cities)
'Bangalore'

References

  • https://en.wikiversity.org/wiki/Python_Concepts/Iteration_and_Iterators
  • https://docs.python.org/2/library/itertools.html
  • https://docs.python.org/3/glossary.html
  • https://docs.python.org/3/library/stdtypes.html#typeiter
  • https://docs.python.org/3/library/functions.html
  • https://docs.python.org/3/howto/functional.html
  • https://wiki.python.org/moin/Iterator

In the next coming blog articles, you will learn about Enumerators, Generators in detail.

Happy Learning!