[Python-ideas] A (meta)class algebra (original) (raw)

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.consolewidget): 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