[Python-3000] Metaclasses in Py3K (original) (raw)

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.



More information about the Python-3000 mailing list