Mixins For Python

last modified: June 12, 2012

Recipe for MixIns in PythonLanguage which allows in-place runtime mixing and unmixing. As a bonus, there is a 'functional-style' mixin function, which preserves the base class.

def mixIn (base, addition):

"""Mixes in place, i.e. the base class is modified.
Tags the class with a list of names of mixed members.
"""
assert not hasattr(base, '_mixed_')
mixed = []
for item, val in addition.__dict__.items():
    if not hasattr(base, item):
        setattr(base, item, val)
        mixed.append (item)
base._mixed_ = mixed

def unMix (cla):

"""Undoes the effect of a mixin on a class. Removes all attributes that
were mixed in -- so even if they have been redefined, they will be
removed.
"""
for m in cla._mixed_: #_mixed_ must exist, or there was no mixin
    delattr(cla, m)
del cla._mixed_

def mixedIn (base, addition):

"""Same as mixIn, but returns a new class instead of modifying
the base.
"""
class newClass: pass
newClass.__dict__ = base.__dict__.copy()
mixIn (newClass, addition)
return newClass

Old recipe

def mixin (existingClass, mixinClass):
        for item, val in mixinClass.__dict__.items():
                if not hasattr(existingclass, item):
                        setattr(existingclass, item, val)

This copies not just functions, but any class members.

Example usage:

class addSubMixin:
        def add(self, value):
                return self.number + value

        def subtract(self, value):
                return self.number - value


class myClass:
        def __init__(self, number):
                self.number = number

Then, at runtime, you can mix any class into any other with:

mixin(myClass, addSubMixin)
myInstance = myClass(4)
myInstance.add(2)
myInstance.subtract(2)

why not do it like this:

class myClass(myClass, myMixin)
  pass

because it is not at runtime.

Person.__bases__ assignment can be made at runtime, for e.g. driver selection.

CAUTION the assignment is to the class.


Good examples of MixIns in the PythonLanguage are in SocketServer module from standard library.

such as:

class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

You just choose the flavors and add them in. Wants forking? add ForkingMixIn.

see also Twisted Python and wxPython for a lot of MixIn uses.

--JuneKim


This does not work for new-style classes, right?


See MixIn.


CategoryPython


Loading...