[Python-ideas] A way out of Meta-hell (was: A (meta)class algebra)

Martin Teichmann lkb.teichmann at gmail.com
Sat Feb 14 10:34:48 CET 2015


Hi everyone,

there seems to be a general agreement that metaclasses,
in the current state, are very problematic. As to what the
actual problem is there seems to be no agreement, but
that there IS a problem seems to be accepted.

Where do we go from here? One great idea is PEP 422.
It just replaces the idea of metaclasses with something simpler.
One point of critique is that it will take ages until people can
actually use it. In order to avoid this, I wrote a pure python
implementation of PEP 422 that works all the way down to
python 2.7. If everyone simply started using this, we could have
a rather smooth transition.

I think that implementing PEP 422 as part of the language only
makes sense if we once would be able to drop metaclasses
altogether. I thought about adding a __new_class__ to PEP 422
that would simulate the __new__ in metaclasses, thinking that
this is the only way metaclasses are used.

I was wrong. Looking at PyQt (to be precise: sip), I realized that
it uses more: it overwrites get/setattr. That's actually a good
usecase that cannot be replaced by PEP 422.
(Sadly, in the case of sip, this is technically not a good usecase:
it is apparently used to give the possibility to write mixin classes
to the right of the mixed-in class in the inheritance chain. Could
someone please convince Phil of PyQt that mixins go to the
left of other base classes?)

The upshot is: my solution sketched above simply does not work
for C extensions that use metaclasses.

This brings me to a completely different option: why don't we
implement PEP 422 in pure python, and put it in the standard
library? Then everyone writing some simple class initializer
would just use that standard metaclass, and so you can use
multiple inheritance and initializers, as all bases will use the same
standard metaclass. Someone writing more complicated
metaclass would inherit from said standard metaclass, if it
makes sense to also have class initializers. For C extensions:
is it possible as a C metaclass to inherit from a python base
class?

I added my pure python implementation of PEP 422. It is
written to be backwards compatible, so a standard library
version could be simplified. A class wanting to have initializers
should simply inherit from WithInit in my code.

Greetings

Martin

class Meta(type):
    @classmethod
    def __prepare__(cls, name, bases, namespace=None, **kwds):
        if namespace is not None:
            cls.__namespace__ = namespace
        if hasattr(cls, '__namespace__'):
            return cls.__namespace__()
        else:
            return super().__prepare__(name, bases, **kwds)

    def __new__(cls, name, bases, dict, **kwds):
        if '__init_class__' in dict:
            dict['__init_class__'] = classmethod(dict['__init_class__'])
        return super(Meta, cls).__new__(cls, name, bases, dict)

    def __init__(self, name, bases, dict, **kwds):
        self.__init_class__(**kwds)


def __init_class__(cls, **kwds):
    pass

WithInit = Meta("WithInit", (object,), dict(__init_class__=__init_class__))

# black magic: assure everyone is using the same WithInit
import sys
if hasattr(sys, "WithInit"):
    WithInit = sys.WithInit
else:
    sys.WithInit = WithInit


More information about the Python-ideas mailing list