[Python-Dev] Things to Know About Super (original) (raw)

Michele Simionato michele.simionato at gmail.com
Thu Aug 28 06:35:08 CEST 2008


Alex Martelli wrote:

What's DebugFrameworkMeta2? I assume it's some kind of mix but I don't see it defined anywhere so I'm having to guess.

Sorry Alex, here is definition which got lost in cut&paste:

DebugFrameworkMeta2 = include_mixin(DebugMeta2, FrameworkMeta2)

I'd like to understand what, in this example, removes the apparent "fragility" (or, lack of flexibility) where DebugMeta2 specifically uses mcl.base -- IOW, if I have another "mixin metaclass" just like DebugMeta2 but called (say) RemotableMeta which does a print "Adding remoting features" and then also calls mcl.base.new(mcl ... just like DebugMeta2, what gets both of their new methods called in the right order?

If you want to reimplement full cooperation between mixins classes you must rework a bit the example, but it does not take a big effort (see later). However my main point is: do we really want cooperative methods? Multiple inheritance of metaclasses is perhaps the strongest use case for multiple inheritance, but is it strong enough? I mean, in real code how many times did I need that? I would not mind make life harder for gurus and simpler for application programmers. I do not think removing cooperation would be so bad in practice. In many practical cases, one could just write the metaclass by hand, in this example something like

class RemotableDebugFrameworkMeta(FrameworkMeta): def new(mcl, name, bases, dic): print "Adding remoting features to %s" % name print "Adding debugging features to %s" % name return FrameworkMeta.new(mcl, name, bases, dic)

Maybe you would need to duplicate a couple of lines and/or to introduce an helper function, but you would have the benefit of having a more explicit metaclass, with a simpler hierarchy (see the appendix for an alternative solution).

Maybe you could help me understand this by giving a fully executable Python snippet using bases[0] instead of the hypothetical base?

To the best of my knowledge base is a valid class attribute, it denotes the "right" class to use as base. Usually it is the same as bases[0], but there is at least one case when it is different, when composing old style and new style classes:

class OldStyle: pass class NewStyle(object): pass class New(OldStyle, NewStyle): pass New.bases[0] <class __main__.OldStyle at 0x777060> New.base <class '__main__.NewStyle'>

Appendix: how to reimplement cooperation in a single-inheritance world

Quoting Raymond: "To achieve substantially the same functionality, you would likely have to re-invent much of what super() does for us automatically, and you would still be imposing constraits on the composed classes that are substantially the same as what you have with inheritance."

Raymond of course is right, but I am arguing that one does not really need to re-invent cooperation because the use case for cooperation are exceedingly rare. Still, if one really wants to reimplement cooperation, here is my take at the challenge:

$ cat cooperative_mixins.py "Implements cooperative mixins using multiple-inheritance only"

everything in three lines

def include_mixin(mixin, cls): # could be extended to use more mixins # traits as in Squeak take the precedence over the base class dic = vars(mixin).copy() # could be extended to walk the ancestors dic['_%s__super' % mixin.name] = cls return type(mixin.name + cls.name, (cls,), dic)

examples:

class FrameworkMeta(type): # example metaclass def new(mcl, name, bases, dic): print "Adding framework features to %s" % name return type.new(mcl, name, bases, dic)

class DebugMeta(type): # mixin metaclass def new(mcl, name, bases, dic): print "Adding debugging features to %s" % name return mcl.__super.new(mcl, name, bases, dic)

class RemotableMeta(type): # another mixin metaclass def new(mcl, name, bases, dic): print "Adding remoting features to %s" % name return mcl.__super.new(mcl, name, bases, dic)

class FrameworkClass(object): metaclass = FrameworkMeta

DebugFrameworkMeta = include_mixin(DebugMeta, FrameworkMeta)

print '**************** creating DebugFrameworkClass' class DebugFrameworkClass(FrameworkClass): metaclass = DebugFrameworkMeta

RemotableDebugFrameworkMeta = include_mixin( RemotableMeta, DebugFrameworkMeta)

print '**************** creating RemotableDebugFrameworkClass' class RemotableDebugFrameworkClass(FrameworkClass): metaclass = RemotableDebugFrameworkMeta



More information about the Python-Dev mailing list