Using Mix-ins with Python

Mix-in programming is a style of software
development where units of functionality are created in a class and
then mixed in with other classes. This might sound like simple
inheritance at first, but a mix-in differs from a traditional class
in one or more of the following ways. Often a mix-in is not the
“primary” superclass of any given class, does not care what class
it is used with, is used with many classes scattered throughout the
class hierarchy and is introduced dynamically at runtime.

There are several reasons to use mix-ins: they extend
existing classes in new areas without having to edit, maintain or
merge with their source code; they keep project components (such as
domain frameworks and interface frameworks) separate; they ease the
creation of new classes by providing a grab bag of functionalities
that can be combined as needed; and they overcome a limitation of
subclassing, whereby a new subclass has no effect if objects of the
original class are still being created in other parts of the
software.

So while mix-ins are not a distinct technical feature of
Python, the benefits of this technique are worth studying.

Python provides an ideal language for mix-in development
because it supports multiple inheritance, supports full-dynamic
binding and allows dynamic changes to classes. Before we dive into
Python, let me admit that mix-ins are old hat. The first time I saw
mix-in programming by that name was when reviewing the now-defunct
Taligent Project, known for its Pink operating system and
CommonPoint application framework. However, since C++ does not
support language feature #2, full-dynamic binding, or language
feature #3, dynamic changes at runtime, I'm not surprised that the
approach didn't bring to fruition all its inventors had hoped
for.

I have also seen another instance of mix-in programming under
a different name. Objective-C has a nifty language feature called
categories that allows you to add and replace methods of existing
classes, even without access to their source code.

This is great for repairing existing system classes and
extending their capabilities. Also, combined with an ability to
load libraries dynamically, categories are quite effective in
improving the structure of applications and reducing code.

The grapevine informs me that Symbolics' object-oriented
Flavors system is most likely the earliest appearance of bona fide
mix-ins. The designers were inspired by Steve's Ice Cream Parlor in
Cambridge, Massachusetts where customers started with a basic
flavor of ice cream (vanilla, chocolate, etc.) and added any
combination of mix-ins (nuts, fudge, chocolate chips, etc.). In the
Symbolics system, large, standalone classes were known as flavors
while smaller helper classes designed for enhancing other classes
were known as mix-ins. A reference can be found on the Web at
www.kirkrader.com/examples/cpp/mixin.htm.

Having paid our respects to the dead (Taligent), nearly dead
(Objective-C) and legendary (Symbolics), let's start digging into
the features that make Python a great language for mix-in
programming. For one, Python supports multiple inheritance. That
is, in Python, a class can inherit more than one class:

class Server(Object, Configurable):
pass

also, Python supports full-dynamic binding. When passing a
message to an object such as:

obj.load(filename)

Python will determine, entirely at runtime, what method to invoke,
based on the name of the message and the class inheritance of obj.
This behavior works as expected and is easy to remember. It
continues to work even if the class inheritance or method
definitions are altered at runtime.

One thing to keep in mind is the order of searching with
regard to multiple inheritance. The search order goes from left to
right through the base classes, and for any given base class, goes
deep into its ancestor classes.

When you create mix-ins, keep in mind the potential for
method names to clash. By creating distinct mix-ins with well-named
methods you can generally avoid any surprises. Lastly, Python
supports dynamic changes to the class hierarchy.

Most Python “things”, whether they are lists, dictionaries,
classes or instances, have a set of accessible attributes. Python
classes have an attribute named __bases__, which is a tuple of
their base classes. Consistent with Python design, you can play
with it at runtime. In the following session with the Python
interactive interpreter seen in Listing 1, we create two classes
and then later change the inheritance. Our person in Listing 1
isn't very friendly so let's change it. In fact, let's change all
people so that we'll never have this problem again:

The first statement above changes the base classes of Person.
By using += (as opposed to =)
we avoid accidentally removing existing base classes, especially if
a future version of the code makes Person inherit from another
class. Also, the funny looking expression,
(Friendly,), specifies a tuple that would
normally simply be enclosed in parenthesis. However, while Python
readily recognizes <If“Courier”>(x,y)<I$f$> as a tuple
of two elements, it recognizes <If“Courier”>(x)<I$f$>
as a parenthesized expression. Appending the comma forces tuple
recognition.

Comment viewing options

Just can't stand seeing this Mixin() function. It's a prime example of pointless code; all it does is call a function on the object passed in.

"In case we want to add to it.." and what might you add to such a generic operation?

If you're going to write that kind of thing, use Java. Those guys expect it.

And mixins make more sense in a language like Java, where interface implementations must be complete. Python's duck typing means you only need as much implementation as you need, and on top of that there's no reason to not just make a runtime object with the necessary method rather then adding methods to an existing object.

CPython supports this, but that isn't necessarily the same as saying Python supports this. You can't just set some __class__ member variable in Jython or IronPython, for example. Tinypy also supports this sort of thing, but with more Lua-esque "metatables."

Trending Topics

Upcoming Webinar

Getting Started with DevOps - Including New Data on IT Performance from Puppet Labs 2015 State of DevOps Report

August 27, 2015
12:00 PM CDT

DevOps represents a profound change from the way most IT departments have traditionally worked: from siloed teams and high-anxiety releases to everyone collaborating on uneventful and more frequent releases of higher-quality code. It doesn't matter how large or small an organization is, or even whether it's historically slow moving or risk averse — there are ways to adopt DevOps sanely, and get measurable results in just weeks.