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

Martin Teichmann lkb.teichmann at gmail.com
Sat Feb 14 12:18:03 CET 2015


Hi Petr, Hi all,

> Just putting it in the standard library doesn't make sense, since it
> would still only be available there from Python 3.5 on (or whenever it
> gets in).
> It really makes more sense to put this into the *real* standard
> metaclass (i.e. `type`).
> The Python implementation can be on PyPI for projects needing the
> backwards compatibility.

That was what I was thinking about. Put the python implementation
on PyPI, so everyone can use it, and finally put the thing in the
standard library.

I actually prefer the standard library over adding it to type, it's
much less a hazzle. Given that there are use cases for metaclasses
not covered by PEP 422, I guess they won't ever be dropped,
so having two complicated things in C are not such a great idea in
my opinion if we can easily write one of them in Python.

> Not really, but you can rewrite the metaclass as a C extension (maybe
> after the Python variant is ironed out).

Well said, and yes, it needs ironing out. I just realized it doesn't work
since I'm calling __init_class__ before its __class__ is set. But this
is a solvable problem: Let's rewrite PEP 422 so that the initialization
is done on subclasses, not the class itself. This is actually a very
important usecase anyways, and if you need your method to be
called on yourself, just call it after the class definition! One line of
code should not be such a big deal.

I also simplified the code, requiring now that decorates __init_subclass__
with @classmethod.

Greetings

Martin

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

    def __new__(cls, name, bases, dict, **kwargs):
        return super(Meta, cls).__new__(cls, name, bases, dict)

    def __init__(self, name, bases, dict, namespace=None, **kwargs):
        super(self, self).__init_subclass__(**kwargs)


class Base(object):
    @classmethod
    def __init_subclass__(cls, **kwargs):
        pass

SubclassInit = Meta("SubclassInit", (Base,), {})


More information about the Python-ideas mailing list