Skip to content

Iterator Design Pattern

Video Lecture

Section Video Links
Iterator Overview Iterator Overview Iterator Overview Iterator Overview 
Iterator Use Case Iterator Use Case Iterator Use Case Iterator Use Case 
Python iter() Function Python iter() Function Python iter() Function Python iter() Function 

Overview

...Refer to Book or Videos for extra content.

Terminology

...Refer to Book or Videos for extra content.

Iterator UML Diagram

Iterator Pattern Overview

Source Code

...Refer to Book or Videos for extra content.

./iterator/iterator_concept.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# pylint: disable=too-few-public-methods
"The Iterator Pattern Concept"
from abc import ABCMeta, abstractmethod

class IIterator(metaclass=ABCMeta):
    "An Iterator Interface"
    @staticmethod
    @abstractmethod
    def has_next():
        "Returns Boolean whether at end of collection or not"

    @staticmethod
    @abstractmethod
    def next():
        "Return the object in collection"

class Iterable(IIterator):
    "The concrete iterator (iterable)"

    def __init__(self, aggregates):
        self.index = 0
        self.aggregates = aggregates

    def next(self):
        if self.index < len(self.aggregates):
            aggregate = self.aggregates[self.index]
            self.index += 1
            return aggregate
        raise Exception("AtEndOfIteratorException", "At End of Iterator")

    def has_next(self):
        return self.index < len(self.aggregates)

class IAggregate(metaclass=ABCMeta):
    "An interface that the aggregates should implement"
    @staticmethod
    @abstractmethod
    def method():
        "a method to implement"

class Aggregate(IAggregate):
    "A concrete object"
    @staticmethod
    def method():
        print("This method has been invoked")

# The Client
AGGREGATES = [Aggregate(), Aggregate(), Aggregate(), Aggregate()]
# AGGREGATES is a python list that is already iterable by default.

# but we can create own own iterator on top anyway.
ITERABLE = Iterable(AGGREGATES)

while ITERABLE.has_next():
    ITERABLE.next().method()

Output

1
2
3
4
5
python ./iterator/iterator_concept.py
This method has been invoked
This method has been invoked
This method has been invoked
This method has been invoked

Example Use Case

...Refer to Book or Videos for extra content.

Example UML Diagram

Iterator Pattern Overview

Source Code

./iterator/client.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
"The Iterator Pattern Concept"


class NumberWheel():  # pylint: disable=too-few-public-methods
    "The concrete iterator (iterable)"

    def __init__(self):
        self.index = 0

    def next(self):
        """Return a new number next in the wheel"""
        self.index = self.index + 1
        return self.index * 2 % 11


# The Client
NUMBERWHEEL = NumberWheel()

for i in range(22):
    print(NUMBERWHEEL.next(), end=", ")

Output

1
2
python ./iterator/client.py
2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 0, 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 0,

New Coding Concepts

Python iter()

Python Lists, Dictionaries, Sets and Tuples are already iterable, so if you want basic iteration for use in a for loop, then you only need to add your objects into one of those and it can be used right away.

1
2
3
4
NAMES = ['SEAN','COSMO','EMMY']
for name in NAMES:
    print(name, end=", ")
#SEAN, COSMO, EMMY,

also, you can instantiate an iterable from the List, Dictionary, Tuple or Set by using the Python iter() method, or its own __iter__() dunder method, and then iterate over that using the __next__() method.

1
2
3
4
5
NAMES = ['SEAN','COSMO','EMMY']
ITERATOR = iter(NAMES)
print(ITERATOR.__next__())
print(ITERATOR.__next__())
print(ITERATOR.__next__())

or

1
2
3
4
5
NAMES = ['SEAN','COSMO','EMMY']
ITERATOR = NAMES.__iter__()
print(ITERATOR.__next__())
print(ITERATOR.__next__())
print(ITERATOR.__next__())

The Python iter() method also can accept a sentinel parameter.

The sentinel parameter is useful for dynamically created objects that are returned from an iterator and indicates where the last item is in the iterator by raising a StopIteration exception.

Usage : iter(object, sentinel)

When using the sentinel, the object passed as the first argument in the iter() method will need to be callable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Doubler():
    count = 1

    @classmethod
    def next(cls):
        cls.count *= 2
        return cls.count

    __call__ = next

ITERATOR = iter(Doubler(), 32)
print(list(ITERATOR))
# Outputs [2, 4, 8, 16]

The __call__ = next line in the example above is setting the default method of the class to be next and that makes the class callable. See Dunder call Method for more information.

Also note that the list being printed at the end is automatically filled from the iterator. The list constructor utilizes the default callable method and the StopIteration exception automatically during its creation without needing to write this in the code.

Summary

...Refer to Book or Videos for extra content.