Python has first class support for OO programming, including advanced techniques such as abstract base classes, multiple inheritance, and traits/mixins.
We earlier saw the syntax for creating a new class:
class LoudTalker(object):
def say(self, message):
print "%s!" % message
shouter = LoudTalker()
shouter.say("Hi") # Prints "Hi!"
Our class is really kind of limited, so let's refactor it a bit:
class LoudTalker(object):
suffix = "!"
def say(self, message):
print "%s%s" % (message, self.suffix)
Here we've added a class attribute suffix. This can be referenced either via the class object (LoudTalker.suffix) or any class instance (self.suffix).
It will also be available via any subclass of LoudTalker:
class SubLoudTalker(LoudTalker):
pass
assert SubLoudTalker.suffix == "!"
class UnsureTalker(LoudTalker):
suffix = "..."
pensively = UnsureTalker()
pensively.say("I'm pretty sure")
Python constructors (actually initializers) are named __init__:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
stephen = Person('Stephen', 27)
What are constructors?
The distinction between constructors and initializers is necessary because there is also a constructor method (named __new__) that is responsible for creating the backing datastructure of the instance. It is rarely used outside of metaclasses.
Sub classes can call parent implementations of methods they've overridden using super. The first argument is the type to "skip" in the hierarchy, the second is the object instance.
class DoubleTalker(LoudTalker):
def say(self, message):
super(DoubleTalker, self).say(message)
super(DoubleTalker, self).say(message)
There is an annoyance when creating "record" style objects in most languages, in that using public properties, while simpler and cleaner, leads to a brittle interface for your class.
What if a property name needs to change? Or some goofball writes time.minute = 1984?
Python neatly sidesteps these issues with it's @property decorator...
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
@property
def age(self): return self._age
@age.setter
def age(self, age):
assert age >= 0
self._age = age
import abc # Abstract Base Classes
class AbstractTalker(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def format(self, message):
return message
def say(self, message):
print self.format(message)
class LoudTalker(AbstractTalker)
def format(self, message):
return "%s!" % message
class Screamer(LoudTalker):
def format(self, message):
return super(Screamer, self).format(message).upper()
An abstract base class cannot be instantiated, and subclasses must implement all methods decorated as abstract. However, you can provide an implementation for abstract methods, which subclasses can use via super. See http://docs.python.org/library/abc.html for more details.
class ShoutFormatterMixin(object):
def format(self, message):
return "%s!" % message
class PublicAddressSystem(ShoutFormatterMixin, AbstractTalker):
def play_music(self, song):
super(PublicAddressSystem, self).say(song.tablature)
Here we have an example of using a Mixin to compose a small bit of functionality into a class through multiple inheritance, and a subclass using the implementation of say in AbstractTalker.
By composing the format functionality of ShoutFormatterMixin with the say functionality of AbstractTalker into our PublicAddressSystem class we have:
We touched on a few advanced topics here. In 90% of day-to-day Python code, simple inheritance is all you need...
... but the next time you find yourself dealing with a fragile or bloated base class, consider using Mixins to separate out the various responsibilities and moving their usage closer to the leaf nodes in your type hierarchy. It tends to make the responsibilities of each individual class much more explicit, and potentially removes unused method implementations from that class.