To teach you about another testing framework for Python, we must first turn our attention to another OOP concept: inheritance.
In Python, every class is declared as extending some base class (except for
object, which has no base class). When you extend a class, your extension (the subclass) inherits all of the methods of the superclass.
To get a feel for why that might be a good thing, let’s think about logging. It’s a common activity to want to record how a program is running. While it’s possible to just use
Suppose we wanted to take our existing logger and have a timestamp on each log. Inheritance makes that easy: we can make a
TimeLogger by extending
Or maybe we’d rather simply count the number of messages logged. We could make a different subclass of
Logger that counted as it ran:
It’s worth remarking on
CountingLogger, which says, “now run the method of my super class (i.e.,
CountingLogger increases its count in
emit to that it doesn’t ever double count things, i.e., if
format were called more than once for each emit, say. Both
Logger.log, i.e., without saying anything, each of these classes had a function
log that called the appropriate methods of the subclass. That is, when
logger.log on the
Logger.log implementation will use
TimeLogger. This phenomenon is called dynamic dispatch.
In dynamic dispatch, the receiver of a method determines which method implementation is called. Each time, it starts at the receiver’s actual type, search that type first and then searching through the base classes. The exact details of Python method resolution order are very complex.
Fortunately, it’s not that important to know all of the details (most of the time). Very often, you can figure out what you need about a class in a library by looking at it and its superclasses. Typically, documentation will guide you well here.
You can ask questions about inheritance using the
issubclass function. For example:
issubclass(A, B) and
isinstance(x, A), then
isinstance(x, B) will be true, also.