Real Mixins September 22, 2006
The normal python paradigm for implementing mixins is using multiple inheritance. Mixin classes take some measures of precaution as of their design (not to interfere with the derivee's MRO as much as possible), but they are essentially just regular classes, being derived from.
This code here creates real mixed-in classes: it actually merges one class into another
CPython specific), taking care of name-mangling, some complications with
everything else. As a side-effect, you can also use it to mix modules into classes.
import inspect def mixin(cls): """ mixes-in a class (or a module) into another class. must be called from within a class definition. `cls` is the class/module to mix-in """ locals = inspect.stack().f_locals if "__module__" not in locals: raise TypeError("mixin() must be called from within a class definition") # copy the class's dict aside and perform some tweaking dict = cls.__dict__.copy() dict.pop("__doc__", None) dict.pop("__module__", None) # __slots__ hell slots = dict.pop("__slots__", ) if slots and "__slots__" not in locals: locals["__slots__"] = ["__dict__"] for name in slots: if name.startswith("__") and not name.endswith("__"): name = "_%s%s" % (cls.__name__, name) dict.pop(name) locals["__slots__"].append(name) # mix the namesapces locals.update(dict)
>>> class SomeMixin(object): ... def f(self, x): ... return self.y + x ... >>> class AnotherMixin(object): ... def g(self): ... print "g" ... >>> >>> class Foo(object): ... mixin(SomeMixin) ... mixin(AnotherMixin) ... ... def h(self): ... print "h" ... >>> f = Foo() >>> obj = Foo() >>> obj.y = 18 >>> dir(obj) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'f', 'g', 'h'] >>> obj.f <bound method Foo.f of <__main__.Foo object at 0x00A96710>> >>> obj.g <bound method Foo.g of <__main__.Foo object at 0x00A96710>> >>> obj.h <bound method Foo.h of <__main__.Foo object at 0x00A96710>> >>> obj.h() h >>> obj.g() g >>> obj.f(4) 22 >>>