Skip to main content
4 of 8
added 2 characters in body
Aaron Hall
  • 400.1k
  • 93
  • 416
  • 342

What is the difference between iterators and generators? Some examples for when you would use each case would be helpful.

A Generator is an Iterator

A generator is a subtype of iterator:

>>> import collections >>> import types >>> def gen(): yield >>> g = gen() >>> isinstance(g, collections.Iterator) True >>> issubclass(types.GeneratorType, collections.Iterator) True 

An Iterator is an Iterable

An Iterator is an Iterable,

>>> issubclass(collections.Iterator, collections.Iterable) True 

which requires an __iter__ method that returns an Iterator:

>>> collections.Iterable() Traceback (most recent call last): File "<pyshell#79>", line 1, in <module> collections.Iterable() TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__ 

Examples of iterables are tuples, lists, sets, dicts, strings, and range objects:

>>> all(isinstance(element, collections.Iterable) for element in ( (), [], {}, set(), '', range(0))) True 

Iterators require a next or __next__ method

In Python 2:

>>> collections.Iterator() Traceback (most recent call last): File "<pyshell#80>", line 1, in <module> collections.Iterator() TypeError: Can't instantiate abstract class Iterator with abstract methods next 

And in Python 3:

>>> collections.Iterator() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class Iterator with abstract methods __next__ 

We can get the iterators from the builtin objects (or custom objects) with the iter function:

>>> all(isinstance(iter(element), collections.Iterator) for element in ( (), [], {}, set(), '', range(0))) True 

The __iter__ function is what is invoked when you attempt to use an object with a for-loop. Then __next__ or next is called on the iterator object to get each item out for the loop. The iterator raises StopIteration when you have exhausted it, and it cannot be reused at that point.

From the docs:

From the Generator Types section of the Iterator Types section of the Built-in Types documentation:

Python’s generators provide a convenient way to implement the iterator protocol. If a container object’s __iter__() method is implemented as a generator, it will automatically return an iterator object (technically, a generator object) supplying the __iter__() and next() [__next__() in Python 3] methods. More information about generators can be found in the documentation for the yield expression.

(Emphasis added.)

So from this we learn that Generators are a (convenient) type of Iterator.

Example Iterator Objects

You might create object that implements the Iterator protocol by creating or extending your own object.

class Yes(collections.Iterator): def __init__(self, stop): self.x = 0 self.stop = stop def __iter__(self): return self def next(self): if self.x < self.stop: self.x += 1 return 'yes' else: # Iterators must raise when done, else considered broken raise StopIteration __next__ = next # Python 3 compatibility 

But it's easier to simply use a Generator to do this:

def yes(stop): for _ in range(stop): yield 'yes' 

Or perhaps simpler, a Generator Expression (works similarly to list comprehensions):

yes_expr = ('yes' for _ in range(stop)) 

They can all be used in the same way:

>>> stop = 4 >>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop), ('yes' for _ in range(stop))): ... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3)) ... 0: yes == yes == yes 1: yes == yes == yes 2: yes == yes == yes 3: yes == yes == yes 

Conclusion

In the vast majority of cases, you are best suited to use yield to define a function that returns a Generator instead or consider Generator Expressions.

However, you can use the Iterator protocol directly when you need to extend a Python object as an object that can be iterated over.

Aaron Hall
  • 400.1k
  • 93
  • 416
  • 342