Using Mix-ins with Python
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.
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:
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.
A useful application of this mix-in is to implement generic code for writing logs (see Listing 3).
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.
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.
|Containers—Not Virtual Machines—Are the Future Cloud||Jun 17, 2013|
|Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer||Jun 12, 2013|
|Weechat, Irssi's Little Brother||Jun 11, 2013|
|One Tail Just Isn't Enough||Jun 07, 2013|
|Introduction to MapReduce with Hadoop on Linux||Jun 05, 2013|
|Android's Limits||Jun 04, 2013|
- Containers—Not Virtual Machines—Are the Future Cloud
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Linux Systems Administrator
- Introduction to MapReduce with Hadoop on Linux
- Senior Perl Developer
- Technical Support Rep
- Weechat, Irssi's Little Brother
- UX Designer
- One Tail Just Isn't Enough
- Android's Limits
- Reply to comment | Linux Journal
43 min 38 sec ago
- Reply to comment | Linux Journal
44 min 5 sec ago
- Replica Watches
3 hours 9 min ago
- Reply to comment | Linux Journal
7 hours 19 min ago
- on the path to understanding
7 hours 23 min ago
- As a fisher,we know that a
1 day 2 hours ago
- All I Say Is Worth Share!
1 day 3 hours ago
1 day 4 hours ago
1 day 7 hours ago
- You should consider visiting
1 day 8 hours ago
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?