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 __slots__
, and
everything else. As a side-effect, you can also use it to mix modules into classes.
Code
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()[1][0].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)
Example
>>> 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
>>>