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_ = mixeddef 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 = numberThen, 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): passYou 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.