[Python-3000] Metaclasses in Py3K

Josiah Carlson jcarlson at uci.edu
Sat Dec 16 21:14:07 CET 2006


Talin <talin at acm.org> wrote:
[snip]
> The main controversies are:

Controversy is perhaps overstating it.  There was disagreement.  But it
wasn't 300 posts and no names were called (that I can remember).

> 1) How generalized the syntax should be.
> 
> Several people, upon seeing your suggestion of "metaclass=Foo", saw an 
> opportunity to use that as a hook for a whole bunch of other 
> class-definition-related features (interface, implements, etc.). This 
> apparently caused a counter-reaction by people who didn't want to see 
> this kind of open-ended generalization (or who objected for other 
> reasons), and resulted in a number of proposals to deliberately limit 
> the syntax in ways that made this kind of expansion was impossible.
> 
> There's also Josiah Carlson's proposal which separates the 'metaclass' 
> function into two parts, the 'dict creation part' and the 'class 
> finishing part'.  (I.e. type= and dict=.) I would rather see them 
> unified, as it makes the class declaration syntax simpler, and it's easy 
> enough to write a metaclass that does one or the other or both.

The reason I offered a mechanism for separating them was because I don't
particularly like the idea of hitting the metaclass twice (particularly
a staticmethod, or base type assignment, which is just one more line of
code for people to forget, and makes metaclass/dictionary reuse more
restrictive), and because metaclasses, so far, haven't needed to be
classes. This particular proposal would make it so that metaclasses
would either need to be a class, or would need to assign to an attribute
on the function metaclass in order to overload the dictionary semantics.
Seems to be an unnecessary requirement.

Here's an alternate mechanism.  As we currently have...

    class Foo(A, B):
        __metaclass__ = mc

Why not extend it with __dict__ (or __metadict__)?  Semantically, it
would be a special name during the execution of the class namespace.
That is, when the class namespace is being executed, when it comes upon
an assignment to __dict__, it performs md(current_locals_dictionary). If
you want it to capture everything (__metaclass__, __slots__, etc.,
assignment), then you make sure to put it at the top.

If people think __metaclass__, __slots__, etc., were a mistake, then
they will probably think __dict__ is also a mistake in this context. 
And I suppose the point of all of this is to get rid of __metaclass__,
so __dict__ is probably right out (though it really is the only
backwards not-breaking syntax on the table).

As an alternative, what about the previously proposed 'is' syntax...

    class Foo(A, B) is type:
        ...

That could be trivially extended to support a tuple syntax for handling
meta dictionary support...

    class Foo(A, B) is type, dict:
        ...

I personally like 'from' rather than 'is', but that's because it sounds
better to me...

"class Foo, subclassing from A and B, from type and dict"


Of course, then we still have __slots__.  Will the madness never end?


> 2) What should the interface on the metaclass look like.
> 
> The general idea is to have the metaclass create a mapping object which 
> is used as the 'locals' dictionary for the suite following the class 
> statement. There would be some special-named function of the metaclass, 
> such as '__metadict__', which would construct a new mapping object. I 
> haven't seen many alternative proposals to this.

Proposal: don't make it a callable on the metaclass.  It forces
metaclasses into being classes, which I think is overly restrictive, or
it forces people to do things like...

    def mymeta(...):
        ...

    mymeta.__metadict__ = mydict


> 4) Backwards compatibility
> 
> It might be possible to retain backwards compatibility if desired. In 
> the proposed scheme, the metaclass is invoked twice - once to create the 
> dictionary, and once to 'finish' the class. Note that the second step is 
> identical to the operation of the current __metaclass__ feature.

Any syntax-based scheme is, by definition, not fully backwards
compatible.  The best we can hope for is for new functionality to not
work as intended in previous Pythons, but still be runnable.

The only syntax that has that ability so far, is to leave __metaclass__
where it is and to add metadict functionality like the following...

    class Foo(A, B):
        __dict__ = ... #or __metadict__

In Python 2.x, the above would basically be ignored.  Which is fine. 
But if we went with either of the two serious options proposed...

    class Foo(A, B, metaclass=...):
        ...

    class Foo(A, B) is ...:
        ...

Then you couldn't share the code between 2.x and 3.x.  I only mention
this because you brought up backwards compatability, and the
syntax-based scheme breaks backwards compatability.

 - Josiah



More information about the Python-3000 mailing list