On Nov 21, 2:11 pm, Joe Strout <[EMAIL PROTECTED]> wrote: > 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? >>> class C(object): ... def __init__(self, a, b): ... self.a = a ... self.b = b ... >>> class D(object): ... def __init__(self, c, d): ... self.c = c ... self.d = d ... >>> class CD(C, D): ... __metaclass__ = AutoGenInitMeta ... >>> >>> x = CD(1,2,3,4) >>> >>> x.a 3 >>> x.b 4 >>> x.c 1 >>> x.d 2 >>> 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 -- http://mail.python.org/mailman/listinfo/python-list