Its not necessary just to iterate over lists in Python with a simple for loop, many things can be used as an iterable, dictionaries, sequences: all objects that implement the __iter__ method!
The __iter__ method in a class returns an iterator which requires the object in question to have a next() method, this method should generate the iterables next value if called and raise an exception if there is no more values.
The range(10) function lists all numbers between 0 and 10 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9). I will implement this function as a class to demonstrate how an iterator can be used as a sequence:
class range2: def __init__(self, stop): self.stop = stop self.start = -1 def next(self): self.start = self.start + 1 if self.start >= self.stop : raise StopIteration return self.start def __iter__(self): return self
It can be used like this:
>>> r = range2(10) >>> print list(r) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
It is important to note that the range2() class returns an iterator object and not a list, to print it out as a list you must explicitly convert it with the list() function.
I am not implementing a function such as range(), range2() is a class!
def __init__(self, stop): self.stop = stop self.start = -1
This initialises the values of start to be -1 and stop to be the value entered as a parameter, it begins at -1 because 0 is printed first, if it was 0 the next value in the iterator would be 1 when it started and that's not what we want!
def next(self): self.start = self.start + 1
Now the important bit for all iterators, they must have a next() method which will be called automatically to generate the "next" value of the iterator. As you can see here our value is increased by 1 each time next is called.
if self.start >= self.stop : raise StopIteration
If the iterator kept calling next() it would keep generating new values forever! To avoid this raise an exception raise StopIteration (specific to iterators) when the value of start is equal or greater than where it is meant to stop. Only values from 0 to n-1 should be generated.
This is the value we want to show, the increasing value, we return it from the next() method.
def __iter__(self): return self
Finally, the __iter__ method is implemented, which returns the iterator itself it can then be used directly in a for loop.
A few examples:
>>> range2(10) <__main__.range2 instance at 0x030122B0> >>> list(range2(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> list(range(5)) [0, 1, 2, 3, 4] >>> for r in range2(5): print r, 0 1 2 3 4
Look out for a more complex implementation of range2() in the snippets section soon!
In part 2, I will implement an iterator as a list of infinite length and show you the benefit of using iterators rather than lists.