Mixins For Python

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


EditText of this page (last edited June 12, 2012) or FindPage with title or text search