Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Iterator Pattern

, 22 Feb 2013
Rate this:
Please Sign up or sign in to vote.
This post will be about the Iterator pattern which is a behavioural pattern.

This post will be about the Iterator pattern which is a behavioural pattern.

The Purpose

The idea behind this pattern is to have an object which you can loop over without needing to know the internal representation of the data. While in Python nothing is private so you can find out the internals of the class, the iterator pattern gives you a standard interface.

I think the best example of an iterator in Python is using a list. As I’m sure you know this is how you would iterate over a list in Python.

list = ["one", "two", "three"]
for number in list:
    print(number)

Which gives the following output:

one
two
three

The goal of this pattern in Python is to be able to do that with a user defined class.

The Pattern

This is quite a small pattern so I will focus more on the implementation detail than the design.

But in the interest of consistency here is the UML diagram.

Iterator Pattern

So the iterator holds a container by aggregation, so it doesn’t own/delete the container, it just has a reference to it.

You can see from this that we are talking about a general collection/data structure here so it is not specific to a list or dictionary for example. The methods on these classes are standard for defining a collection/iterator interface in Python and I will go through these in more detail next.

Python Protocols

Protocols are the name given in Python, for the interface you need to make a certain kind of object. These are not a formal requirement like an interface, but more of a guide.

They focus on pythons magic methods, which are those with double underscores surrounding the name.

I’m going to briefly talk about the protocols for mutable and immutable containers, and iterators.

Immutable Containers

An immutable container is one where you cannot change the individual items. For this you only need to be able to get the length of it and access individual items.

The Python magic methods for an immutable container are.

def __len__(self):
    """ Returns the length of the container. Called like len(class) """
def __getitem__(self, key):
    """ 
    Returns the keyth item in the container. 
    Should raise TypeError if the type of the key is wrong.
    Should raise KeyError if there is no item for the key. 
    Called like class[key]
    """

Again the exceptions mentioned in __getitem__ are convention, but it’s important for writing idiomatic Python.

Mutable Containers

As you might expect a mutable container has the same methods for accessing items as an immutable container, but adds ways of setting or adding them.

Here are the magic methods.

def __len__(self):
    """ 
    Returns the length of the container. 
    Called like len(class) 
    """
def __getitem__(self, key):
    """ 
    Returns the keyth item in the container. 
    Should raise TypeError if the type of the key is wrong.
    Should raise KeyError if there is no item for the key. 
    Called like class[key]
    """

def __setitem__(self, key, value):
    """ 
    Sets the keyth item in the container. 
    Should raise TypeError if the type of the key is wrong.
    Should raise KeyError if there is no item for the key. 
    Called like class[key] = value
    """

def __delitem__(self, key):
    """ 
    Deletes an item from the collection.  
    Should raise TypeError if the type of the key is wrong.
    Should raise KeyError if there is no item for the key. 
    Called like del class[key]
    """

For an immutable container you probably want to have some way of adding elements too. For a list/array style container this might be in the form of a function append(self, key) or for a dictionary/table type container it might be using the __setitem__(self, key, value) function.

There are other functions you can add such as __reversed__(self) and __contains__(self, item) but they are not needed for core functionality. They are very well described here.

Iterators

The protocol for an iterator is very simple.

def __iter__(self):
    """
    Returns an iterator for the collection.
    Called like iter(class) or for item in class:
    """
def next(self):
   """
   Returns the next item in the collection.
   Called in a for loop, or manually.
   Should raise StopIteration on the last item.
   """

The __iter__ function will typically return another iterator object or return itself. Note in Python 3 next() is renamed __next__().

An Example Usage

Here is an example of how you might implement a simple iterator. My iterator will loop over a collection in reverse.

#==============================================================================
class ReverseIterator(object):
    """ 
    Iterates the object given to it in reverse so it shows the difference. 
    """

    def __init__(self, iterable_object):
        self.list = iterable_object
        # start at the end of the iterable_object
        self.index = len(iterable_object)

    def __iter__(self):
        # return an iterator
        return self

    def next(self):
        """ Return the list backwards so it's noticeably different."""
        if (self.index == 0):
            # the list is over, raise a stop index exception
            raise StopIteration
        self.index = self.index - 1
        return self.list[self.index]

Note this only has the two functions needed for the iterator protocol, as these are all that are needed.

Hopefully it is fairly obvious what these functions do. The constructor takes some iterable and caches it’s length as an index. The __iter__ returns the ReverseIterator as it has a next() function. The next function decrements the index and returns that item, unless there is no item to return at which point it raise StopIteration.

In my example the ReverseIterator is used by a Days class, given here.

#==============================================================================
class Days(object):

    def __init__(self):
        self.days = [
        "Monday",
        "Tuesday", 
        "Wednesday", 
        "Thursday",
        "Friday", 
        "Saturday", 
        "Sunday"
        ]

    def reverse_iter(self):
        return ReverseIterator(self.days)

This is just a wrapper for a list of the days of the week, which has a function to return an iterator.

This is how these classes are used.

#==============================================================================
if (__name__ == "__main__"):
    days = Days()
    for day in days.reverse_iter():
        print(day)

Note I could have used a __iter__() function in Days instead of reverse_iter(). This would then have been used like this.

#==============================================================================
if (__name__ == "__main__"):
    days = Days()
    for day in days:
        print(day)

My reason for not doing this is that this does not make it clear that you are reversing it.

The output from either of these is.

Sunday
Saturday
Friday
Thursday
Wednesday
Tuesday
Monday

This code for this can be found in this file.

Here is the specific UML for these classes.

Specific Iterator Pattern

All of the code for the design patterns can be found here.

Thanks for reading.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

David Corne
Software Developer
United Kingdom United Kingdom
I am a C++ developer with a strong interest in Python, C#, and Qt. I work on a native C++ application which uses COM to call C# in order to use a WPF GUI.
 
While I develop an application using WPF exclusivly for windows, I am a big linux user. My favourite distro at the moment is Linux Mint, and I love the delights of the command line,.
 
If you've read something of mine and you enjoyed it, check out my blog.
 
I am also active on various other sites, listed below.

Coding Sites

  • BitBucket where I keep the majority of my projects.
  • GitHub where I have a few older projects. This includes my current long term project, I'm writing a book about design patterns in python. Find the repository here and blog posts about individual patterns here
  • Stackoverflow I'm not too active on stackoverflow, I'm more of a listener.
  • coderwall and coderbits two profile compilation websites.

Social Sites

Follow on   Twitter   Google+

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140921.1 | Last Updated 22 Feb 2013
Article Copyright 2013 by David Corne
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid