“There is nothing either good or bad, but thinking makes it so.” ― William Shakespeare, Hamlet
Contents
1. Introduction
A nested or inner class is contained within another class. This could be for reasons of encapsulation, where the inner class is not useful by itself.
Here is an example of how the classes are laid out.
class A: class B: pass pass
An inner class in python is a distinct entity in that it does not automatically get access to the outer class members in any special way.
2. Implementing an Iterator without an Inner Class
Let us examine the use of an inner class to implement an iterator. An iterator is an object that can be used in a for-loop for iterating over a collection.
Consider this example of Cats class which stores a bunch of cat names. You can use the add() method to add a cat. Since the class also implements the __iter__() special method to return an iterator, an instance of this class can be used directly in a for-loop.
An iterator is an object that supports the next() method and raises the StopIteration exception when the end of the collection is reached.
class Cats: def __init__(self): self.cats = [] self.cur = 0 def add(self, name): self.cats.append(name) return self def __iter__(self): return self def next(self): i = self.cur if i >= len(self.cats): raise StopIteration self.cur += 1 return self.cats[i]
Here is how you can use this class.
a = Cats() a.add('joe').add('jack').add('fink').add('dink') for c in a: print c # prints joe jack fink dink
3. Making the Iterator an Inner Class
Now the above implementation has a design fault. It combines the functionalities of the Cats object with an iterator object. Functionally the two entities are separate and this should be reflected in code. Let us do this by moving the iterator functionality into a separate class. And since the iterator class is specialized to the Cats class, it will not be useful outside of the Cats class and we should make it an inner class.
Here is the implementation where the iterator has been separated into the _cat_iter class with the required implementation of the next() method. Also notice that the __iter__() method returns an instance of the iterator class.
class Cats: class _cat_iter: def __init__(self, cats): self.cats = cats self.cur = 0 def next(self): i = self.cur if i >= len(self.cats): raise StopIteration self.cur += 1 return self.cats[i] def __init__(self): self.cats = [] def add(self, name): self.cats.append(name) return self def __iter__(self): return Cats._cat_iter(self.cats)
Since the interface to the Cats class did not change, there is no change in the client code.
a = Cats() a.add('joe').add('jack').add('fink').add('dink') for c in a: print c
Conclusion
Python does have support for nested or inner classes. These classes can be used when you do not want to expose the class, and it is closely related to another class.