[Python-ideas] A (meta)class algebra

Petr Viktorin encukou at gmail.com
Fri Feb 13 11:55:37 CET 2015


On Fri, Feb 13, 2015 at 5:43 AM, Martin Teichmann
<lkb.teichmann at gmail.com> wrote:
> Hi Thomas, Hi list,
>
>> 1. More magic around combining metaclasses would make it harder to follow
>> what's going on. Explicit (this has a metaclass which is a combination of
>> two other metaclasses) is better than implicit (this has a metaclass which
>> can combine itself with other metaclasses found from the MRO).
>
> Well I disagree. If you follow this rule, everyone who uses a metaclass
> needs to know how to combine it with another metaclass. This
> leads to code like (I'm citing IPython.qt.console.console_widget):
>
> class ConsoleWidget(MetaQObjectHasTraits('NewBase',
> (LoggingConfigurable, QtGui.QWidget), {})):
>
> which, in my opinion, is hard to read and grasp. Everyone has to
> be taught on how to use those metaclass mixers.
>
>> 2. I *want* it to be hard for people to write multiple-inheriting code with
>> multiple metaclasses. That code is most likely going to be a nightmare for
>> anyone else to understand, so I want the person creating it to stop and
>> think if they can do it a different way, like using composition instead of
>> inheritance.
>
> I'm completely with you arguing that inheritance is overrated and
> composition should be used more often. Yet, I actually think that
> the above example is actually not such a bad idea, why should a
> QWidget not be LoggingConfigurable, and have traits? Whatever
> that might mean in detail, I think most programmers do get
> what's going on. It's just that
>
> class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
>
> is much more readable. Is there magic going on in the background?
> Well, to some point, yes. But it is explicit magic, the authors of
> the metaclasses have to explicitly write an __add__ method to
> do the metaclass combination. In this regard, it's actually more
> explicit than the metaclass mixing function as above and
> also mentioned by Anthony (how do I respond to two mails on a
> mailing list at the same time?), which typically stuff together
> metaclasses without thinking to much about them.
>
> I also don't see how this is harder to refactor. If everyone has
> to explicitly combine to metaclasses if needed, you will have
> to change all that code if the details of combining the metaclasses
> change. In my version, one would just have to touch the metaclass
> code.
>
> This becomes even worse if you want to add a metaclass to a class.
> Then you have to change every class that inherits from it. Now you
> say that code with multiple metaclasses is a bad thing in general,
> but I don't agree. There are very simple examples where it makes
> a lot of sense. Say, you want to write a class with traits that
> implements an abstract base class. Wouldn't it be cool if you could
> write

This is the one thing keeping metaclasses from being truly general –
using them limits inheritance.

The problem is that metaclasses are too powerful.
They can change the C-level type; you can't automatically combine two
metaclasses that do this.
They can change the class statement's namespace (via __prepare__).
Again, you can't automatically combine these.
In these cases you really need knowledge of *both* metaclasses to
determine if you're mixing them correctly. It can't really be done in
a method on one of the classes. (And btw, __add__ is a terrible name
for the method. If it's meant to be called implicitly by Python the
"+" sugar is unnecessary. Something like __metaclass_combine__ would
be a better name.)

So, I argue that automatically combining metaclasses makes sense if at
least one of the metaclasses is "simple": it only mutates the class's
attributes, after the class is created. In other words, it overrides
__init__ but not __new__ or __prepare__. Such metaclasses are very
useful – registrars, ORM tables, etc. The problem that limits their
adoption is that they can't be freely combined.
Similar things can be done by decorators, the problem being that the
decorator needs to be repeated on every subclass. (But this may not be
a huge problem: in many "Registrar" cases, explicit is better than
implicit.)

Anyway, perhaps "simple" metaclasses can be combined automatically? Or
there could be an instantiation hook to enable these classes without
limiting subclassing?


More information about the Python-ideas mailing list