Using Mix-ins with Python

An explanation of the mix-in programming style as applied in Python.
MySQLdb Cursor Mix-ins

The most straightforward way to apply mix-ins is at design time within the construction of a module. One of the more famous third-party modules for Python, MySQLdb, does exactly this.

Python defines a standard programmatic interface for database access named DB API (http://www.python.org/topics/database/). Andy Dustman's MySQLdb module implements this interface so that Python programmers can make connections and send queries to a MySQL server. It can be found at http://dustman.net/andy/python/MySQLdb/.

MySQLdb provides three major features for the cursor objects it creates. It reports warnings when necessary; it stores result sets on the client side or uses them on the server side as needed, and it returns results as tuples (e.g., immutable lists) or dictionaries.

Rather than combining all of these into one monolithic class, MySQLdb defines mix-in classes for each of them:

class CursorWarningMixIn:
class CursorStoreResultMixIn:
class CursorUseResultMixIn
class CursorTupleRowsMixIn:
class CursorDictRowsMixIn(CursorTupleRowsMixIn):

Remember that mix-ins are classes, so they can take advantage of inheritance, as we see with CursorDictRowsMixIn, which inherits CursorTupleRowsMixIn.

None of the mix-ins above can stand on their own: a BaseCursor class provides the required core functionality for any type of cursor. Using these mix-ins in combination with BaseCursor, MySQLdb offers every combination of warnings, storage and result types (eight in all). When creating a database connection, you can pass the cursor class you desire:

conn = MySQLdb.connection (cursorclass=MySQLdb.DictCursor)

Mix-ins don't only help in the creation of MySQLdb itself. They also make it more extensible by allowing you to pick and choose features for your own custom cursor classes.

Note that these class names are suffixed with MixIn to emphasize their nature. Another common convention is to append “-able” or “-ible” to the end of the name as in Configurable or NamedValueAccessible.

NamedValueAccessible

Let's use that last one as an example. The NamedValueAccessible mix-in adds the method valueForKey( ) to whatever class with which it is joined. For obj.valueForKey(name), this method will return one of the following:

  • obj.name( )

  • obj._name( )

  • obj.name

  • obj._name

In other words, valueForKey( ) looks for methods or attributes, either public or private, in order to return a value for the given key. The design of this method reflects the fact that Python objects often provide information through both attributes and methods. See Listing 2 for the implementation.

Listing 2. A Mix-in for Uniform Value Access

A useful application of this mix-in is to implement generic code for writing logs (see Listing 3).

Listing 3. Applying the NamedValueAccessible Mix-in for Logging

By simply adding new keys to the logColumns( ) method, the log can be expanded without having to modify the code that generates it, which is found in logEntry( ). More importantly, you can imagine that logColumns( ) could read its list of fields from a simple configuration file.

The transaction object itself is free to provide the given values via either methods or attributes, due to the flexibility of the valueForKey( ) method. Making mix-ins flexible increases their utility and is an art that can be developed over time.

Mixing It in after the Fact

So far we have seen examples of using mix-ins during the construction of classes. However, Python's dynamic nature also allows us to mix in functionality at runtime. The simplest technique for doing so is to modify the base classes of the given class, as described earlier. A function allows us to keep this operation opaque and enhance it later if need be:

def MixIn(pyClass, mixInClass):
    pyClass.__bases__ += mixInClass

Let's consider a situation that makes the utility of MixIn( ) obvious. In the construction of internet applications, keeping domain classes separate from interface classes is generally a good idea. Domain classes represent the concepts, data and operations of a specific application. They are independent of operating system, user interface, database, etc. Some writers refer to domain objects as business objects, model objects or problem space objects.

Keeping the domain and interface separate makes sense for various reasons. An individual focus is created for two key areas that are largely independent: What is the subject material of the problem? And, how should that be presented? New interfaces can be constructed without modifying or rewriting the domain classes. In fact, multiple interfaces can be provided.

Domain classes for a story publishing system might include Story, Author and Site. These classes contain essential attributes (such as title, body, name, e-mail, etc.) and various operations (save, load, publish, etc.).

One interface for such a system could be a web site that allows users to create, edit, delete and publish these stories. When developing such a site, it would be useful if our domain classes, such as Story, have the methods renderView( ) and renderForm( ), which write HTML for either displaying the story or editing it with a form.

Using mix-ins, we can develop such functionality outside of the domain classes:

class StoryInterface:
    def renderView(self):
        # write the HTML representation of the story
        pass
    def renderForm(self):
        # write the HTML form to edit the story
        pass

And within the code that backs the web site, mix it in like so:

from MixIn import MixIn
from Domain.Story import Story
MixIn(Story, StoryInterface)
If you decide to create a GUI interface for the publishing system, you don't have to take the HTML machinery with you (or vice versa). The domain classes focus on providing necessary data and operations, ensuring that when developing the GUI, you will have what you need.

One could argue that a new class might be created to bring the two together:

class StoryInterface:
    ...
from Domain.Story import Story
class Story(Story, StoryInterface): pass

Or one could argue that StoryInterface might be made a subclass of Story in order to achieve the same benefit. However, consider the case when Story already has other domain subclasses:

class Story: ...
class Editorial(Story): ...
class Feature(Story): ...
class Column(Story): ...
Existing subclasses of Story are in no way affected by simply creating a new Story class or subclass. But a dynamic mix-in for Story will also affect Editorial, Feature and Column. That is why many times the static approach does not work in practice, thereby making the dynamic approach not only clever, but necessary.

Also, consider the case where Story objects are created in parts of the code where Story is hard-coded. While poor practice, this is common. In this situation, creating subclasses of Story will have no effect on the code that ignores them.

One warning regarding dynamic mix-ins: they can change the behavior of existing objects (because they change the classes of those objects). This could lead to unpredictable results, as most classes are not designed with that type of change in mind. The safe way to use dynamic mix-ins is to install them when the application first starts, before any objects are created.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Just can't stand seeing this

Grubert's picture

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.

Much abo about very little.

"Python supports dynamic changes to the class hierarchy."

Donny Viszneki's picture

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."

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix