solving the metaclass conflict

Phillip J. Eby pje at telecommunity.com
Sun Jun 8 14:48:04 EDT 2003


mertz at gnosis.cx (David Mertz) wrote in message news:<mailman.1055044454.21616.python-list at python.org>...
> |>  --> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/204197
> |FYI, note that your implementation doesn't:
> 
> 
> |* Handle non-type metatype roots (e.g. ExtensionClass and MESS)
> 
> Hmmm...  I never thought about this, and don't really know what the
> issues are.  Where does this come up?

It's related to the multi-level metaclass issue.  See below.


> > |* Handle multi-level metaclasses (e.g. metametaclasses)
> 
> This I don't get at all.  I've never used a metametaclass (and doubt I
> will).  

'type' is a metametaclass.  It's also a metametameta...class. 
ExtensionClass is similar.  A "root metatype" is a metaclass that is
its own class.

> But even assuming I did, the type() of a metaclass resolves to
> such a metametaclass, and the proposed code should work fine.
> 
> Is something else intended here?

class MetaMeta1(type): pass
class MetaMeta2(type): pass

class Meta1(type): __metaclass__ = MetaMeta1
class Meta2(type): __metaclass__ = MetaMeta2

class Base1: __metaclass__ = Meta1
class Base2: __metaclass__ = Meta2

class C(Base1,Base2):  # conflict at metameta level

The conflict can be resolved, because the MetaMetas share the same
root metatype: type.  You can resolve any conflict as long as all
meta-to-the-Nth classes eventually merge into the same root metatype.

Let's say, though, that MetaMeta2 was a subclass of ExtensionClass:

>>> type(type)
<type 'type'>

>>> type(type(type))
<type 'type'>

>>> type(ExtensionClass)
<ExtensionClass 'ExtensionClass'>

>>> type(type(ExtensionClass))
<ExtensionClass 'ExtensionClass'>

Trying to resolve the metatype conflict here leads to infinite
recursion, and a confused user of the metaclass generation code.  I
wrote code to catch this (i.e. that taking the list of metaclasses of
a list of bases results in the same list of types) so that a
meaningful error message could be given.

Note, by the way, that if 'ClassType' is removed from the list of
metaclasses, then classic classes do not conflict with either
ExtensionClass or type, so a root metatype can be resolved.


 
> |* Remove unneeded metaclasses that are a subclass of another listed
> |metaclass
> 
> I think Eby means the reverse of what he actually writes.  It's not the
> subclass but the superclass that might get removed, methinks.

Yes, typo, sorry.  I meant that *have* a subclass *as* another listed
metaclass.


> Now admittedly, the result <class 'noconflict._BC'> should be just as
> good.  But I don't think anything really behaves differently as is.
> 
> Of course:
> 
>     M = _generatemetaclass((),(A,B,C),0)
>     #-> TypeError: MRO conflict among bases B, C, A
> 
> Which is a bad thing.  Is that the real concern?

That's one part of it, certainly.  But I also prefer algorithms like
this to be as visibly deterministic as possible.  2.2's MRO algorithm
is hard enough to predict, without the metaclass generator throwing up
additional obstacles to clarity.


> > |* Reuse all generated metaclasses (You're memoizing on a set of base
> |classes, rather than on the set of unique metaclasses.)
> 
> This I don't get either.  What's identifies a unique metaclass apart
> from an (ordered) collection of base metaclasses? 

The memoization in Michele's recipe is on the base *classes*, not the
classes of the base classes, at least if I understood it correctly.


> |PEAK's 'peak.util.Meta' module contains a more-complete implementation
> |of this technique.  (See http://peak.telecommunity.com/ for info on
> |PEAK.)
> 
> I plan on including something like this in gnosis.magic (which is public
> domain).  May I take (and modify) peak.util.Meta for this purpose if I
> decide I want to?  I did not notice a prominent notice of its
> copyright/license terms on the web site.

It's available under the PSF license or the ZPL, as you choose. 
Neither is compatible with "public domain", however, if I understand
correctly.

As I mentioned, though, when I first wrote this, I thought it would be
useful outside of the AOP tool it was written for.  And then when I
actually got to using metaclasses, I found that it was easy enough to
create a new metaclass by hand, and not worth the trouble of even
importing a function to generate one automatically!  I think perhaps
Guido's decision not to generate the metaclass automatically was a
wise one in more ways than one.




More information about the Python-list mailing list