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

Martin Teichmann lkb.teichmann at gmail.com
Tue Feb 17 11:10:00 CET 2015


Hi Petr, Hi Andrew, Hi list,

(responding to my own post, as I cannot respond to Petr's and Andrew's
at the same time)

> - is it desirable to have a programmatic way to combine metaclasses?

there seems to be general agreement that there should be no automatic
combination of metaclasses, while many think that explicit combination
should be possible.

The only way to do that currently is by subclassing existing metaclasses.
This is typically done by the user of the metaclass, not its author.
It would be a nice thing if an author could do that. Petr argues s/he
already can, by subclassing. Unfortunately, this is not so easy as this
means that every user of such a class then has to use all inherited
metaclasses.

As an example: imagine you have a metaclass that mixes well with
PyQt's metaclass. Then you could simply inherit from it. But this
would mean that every user of you class also has to install PyQt,
no matter if the code has anything to do with it.

Now Thomas argues that this should simply not be done at all.
I understand his argument well that multiple inheritance across
project boundaries is probably a bad idea in most cases.

But I have an example where it does make a lot of sense: ABCs.
It would just be nice if one could implement and ABC which is a
QObject. That's what ABCs are for. Sure, you can do this by
registering the class, but this means you loose a lot like already
predefined methods of an ABC. But maybe we can just
special-case ABCs.

> - if yes, how should that be done?

So I proposed two ways how this could be implemented:
One is to change the current metaclass combination code to check
whether a class is a subclass not by PyType_IsSubtype, but by
PyObject_IsSubclass, meaning that the __subclasshook__ is
called. This has the advantage of being only a minimal change
to the python core, I'm not sure how many people out there
new at all how the subclass detection was done.

It is also very systematic: it is very analog to ABCs, just the
other way around. An AbstractMetaClass would simply now
which classes exist and how they can be combined. Either by
inheriting from AbstractMetaClass, or by registering a foreign
class.

This way has the drawback of being rather complex on the python
side. But actually, I prefer complexity on the Python- over
complexity on the C side.

This proposal is at
https://github.com/tecki/cpython/commits/metaclass-issubclass

My other proposal was to have a way to explicitly combine
(meta)classes, by having a method in the type object (I originally
called that __add__, people didn't like that so I renamed it to
merge).

This idea has somewhat more code on the C side, but is simpler
on the python side. It might also be easier to grasp by newbies.
Otherwise it is about as powerful as the other idea.

This proposal is at
https://github.com/tecki/cpython/commits/metaclass-merge

Once we have one of my ideas implemented, I think we can
implement PEP 422 in the standard library. Petr claims that this
is just doing simple things with complex code. I disagree, I think
that the current metaclass code in the python interpreter is already
incredibly context, and just throwing more code at it is actually
a very complex solution. Sure, on the python side all looks pretty,
but only because we swept all complexity under the C carpet.
But I won't fight for that point. What I am fighting for is to modify
PEP 422 in a way that it can be backported to older versions of
python and be put on PyPI. This means that __init_class__ should
become __init_subclass__, meaning that only subclasses are
initialized. (__init_class__ doesn't work properly, as super() won't
work. This problem already exists in the zope version).

There is also a completely different way we could solve all those
problems: abandon metaclasses for good, and replace them
eventually with a PEP 422 like approach. This would mean that
on the long run the following should be added:

- a __new_subclass__ as a replacement for the metaclass __new__.
This shows that it is necessary to operate on subclasses, otherwise
one will have a hard time to create the class itself...

- __subclasscheck__ and __instancecheck__ must be called on the
class, not the metaclass

- sometimes metaclasses are used to add methods to the class only,
that cannot be reached by an instance. A new @classonlymethod
decorator should do that.

- I'm sure I forgot something.

A pure python version of this proposal (it would go into C, for sure)
can be found here:
https://github.com/tecki/metaclasses

Note that this is much more complicated as it would need to be if
only the original PEP422 ideas were implemented.

This was a long post.

Enjoy.

Greetings

Martin


More information about the Python-ideas mailing list