[Python-Dev] Things to Know About Super

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