[Types-sig] Re: Meta-classes discussion starter

Just van Rossum just@letterror.com
Tue, 1 Dec 1998 12:16:04 +0100


At 11:12 PM -0500 11/30/98, Barry A. Warsaw wrote:
>I haven't seen any comments on my posted syntax proposal; perhaps it's
>gross, but anyway I liked it.  Or maybe because it's buried in a long
>egroups posting:
>
>    <http://www.egroups.com/list/python-classes/54.html>

(Hm, since your post contains mostly code, and egroups screws it up
majorly, I cannot read it... If you still have it, I'd sure appreciate it
as prvt mail.)

>    JvR> Which means you specify a metaclass like this:
>
>    | class MyClass:
>    | 	__class__ = MyMetaClass
>
>    JvR> Not exactly pretty, but at least it doesn't look like
>    JvR> inheritance. It also means that if you want to subclass from
>    JvR> MyMetaClass you write
>
>Not exactly ugly either!  Very similar to my proposal except 1) I
>chose __nature__ as the magic attribute, and 2) I didn't actually
>implement it like you did, just sort of modeled it in Python.

I initially chose __class__ just because Guido's hook uses that, too. Turns
out that because of that the two Brotherly Hooks complement each other
wonderfully!

>    JvR> (If anyone wants
>    JvR> to play, I could post a patch.)
>
>Yes, please do!

See below... It's a against the current state of Python 1.5.2b1. It should
be 100% compatible with the existing hooks (ie. I tried carefully not to
break things).

Just


- - - - - 8< - - - - - - - - - - - - -
*** ceval.c     Tue Dec  1 05:44:30 1998
--- jmetapatch/ceval.c  Tue Dec  1 05:44:12 1998
***************
*** 2672,2681 ****
--- 2672,2738 ----
        if (!PyString_Check(name)) {
                PyErr_SetString(PyExc_SystemError,
                                "build_class witn non-string name");
                return NULL;
        }
+       /*      __BEGIN__ of Just's Hook
+
+               Guido's metahook is defined as follows:
+                       - if one of the bases has a __class__ attribute (but is
+                         itself not a class!), call that thing with (name,
+                         bases, methods)
+               In addition I propose almost the opposite:
+                       - if the "methods" dict (__dict__ from the Python
+                         perspective) has a __class__ key, call that thing
with
+                         (name, bases, methods)
+
+               This means that metaclasses are not special anymore, and
+               you have to specify a metaclass *explicitly* to get meta
+               behaviour. Example:
+
+                       class Foo:
+                               __class__ = MyMetaClass
+
+               as apposed to
+
+                       MyMeta = MyMetaClass("MyMeta", (), {})
+
+                       class Foo(MyMeta): pass
+
+               as it is with Guido's hook.
+
+               Reasons for this new hook:
+                       - Guido's hook abuses inheritance syntax, making it
+                         impossible to inherit from metaclasses without
special
+                         trickery.
+                       - implementing Meta stuff seems cleaner. Or maybe it's
+                         just me...
+
+               At first I thought Guido's hook would not be compatible with
+               mine, but they work together beautifully: inheritance works
+               just like you would expect.
+       */
+       {
+               PyObject *callable = NULL;
+               callable = PyDict_GetItemString(methods, "__class__");
+               if (callable) {
+                       PyObject *args;
+                       PyObject *newclass = NULL;
+                       PyDict_DelItemString(methods, "__class__");
+                       args = Py_BuildValue(
+                               "(OOO)", name, bases, methods);
+                       if (args != NULL) {
+                               newclass = PyEval_CallObject(
+                                       callable, args);
+                               Py_DECREF(args);
+                       }
+                       return newclass;
+               } else {
+                       PyErr_Clear();
+               }
+       }
+       /*      __END__ of Just's Hook */
        n = PyTuple_Size(bases);
        for (i = 0; i < n; i++) {
                PyObject *base = PyTuple_GET_ITEM(bases, i);
                if (!PyClass_Check(base)) {
                        /* Call the base's *type*, if it is callable.