I have a function that takes a reference to a class, and then
instantiates that class (and then does several other things with the
new instance). This is easy enough:
item = cls(self, **itemArgs)
where "cls" is the class reference, and itemArgs is obviously a set of
keyword arguments for its __init__ method.
But now I want to generalize this to handle a set of mix-in classes.
Normally you use mixins by creating a class that derives from two or
more other classes, and then instantiate that custom class. But in my
situation, I don't know ahead of time which mixins might be used and
in what combination. So I'd like to take a list of class references,
and instantiate an object that derives from all of them, dynamically.
Is this possible? If so, how?
Thanks,
- Joe
I wrote this a while ago. I sort of regret it though. Mixins could
(and I will argue should) be avoided most of the time by delegating to
other objects with less functionality. Utilizing many mixin classes
tends to just make gigantic classes. This is a huge violation of the
"Single Responsibility Principle". That isn't to say that I don't
think there is a place for multiple inheritance. Multiple inheritance
to the point where it is easier to write a metaclass to automatically
generate your __init__ method than it is to write it yourself is a
good indicator that you have gone too far. Which is what I did.
code:
import inspect
class AutoGenInitMetaError(Exception):
""" Exception is raised if AutoGenInitMeta cannot auto-generate a
constructor for a class because of conflicting parameters in base
classes.
"""
pass
class AutoGenInitMeta(type):
""" Meta-Class for automatically generating __init__ method for a
class
with multiple mixin base classes.
"""
def __new__(cls, name, bases, assoc):
if "__init__" in assoc:
return super(AutoGenInitMeta, cls).__new__(cls, name,
bases, assoc)
args = ['self']
dargs = []
defaults = []
tmpl = []
varg = None
vkwarg = None
tmpl = ["def __init__%s:\n"]
for base in bases[::-1]:
a, va, vkw, d = argspec = inspect.getargspec
(base.__init__)
argspecfmt = inspect.formatargspec(*argspec[:3])
if d:
num_d = len(d)
args += a[1:-num_d]
defaults += d
dargs += a[-num_d:]
else:
# remember to stip off self
args += a[1:]
if va is not None:
if varg is not None:
raise AutoGenInitMetaError(
"There must be only one `*` arg in base
constructors"
)
varg = va
if vkw is not None:
if vkwarg is not None:
raise AutoGenInitMetaError(
"There must be only one `**` arg in base
constructors"
)
vkwarg = vkw
tmpl.append(" %s.__init__%s\n"%(base.__name__,
argspecfmt))
tmpl = "".join(tmpl)
argspec = (args + dargs, varg, vkwarg, defaults)
exec tmpl%inspect.formatargspec(*argspec) in globals(), assoc
return super(AutoGenInitMeta, cls).__new__(cls, name, bases,
assoc)
How do you use it?
.... def __init__(self, a, b):
.... self.a = a
.... self.b = b
........ def __init__(self, c, d):
.... self.c = c
.... self.d = d
........ __metaclass__ = AutoGenInitMeta
....
Notice that the arguments to D came before C. So you have to list the
classes in reverse order of how you want the arguments listed.
I post it as an example of a "neat python trick". Even the "neat"
might be self indulgence. I encourage anybody tempted to use this to
refactor/redesign instead.
Matt