Re: [Python-ideas] A (meta)class algebra
Hi everyone, I guess I need to give an example to show what my proposal is about. Imagine a metaclass which registers all classes based on it: class MetaRegistrar(type): def __init__(self, name, bases, dict): super().__init__(name, bases, dict) Registrar.classlist.append(self) class Registrar(object, metaclass=MetaRegistrar): classlist = [] now every subclass of Registrar is registered into its classlist. Imagine now you want to register a class which inherits another metaclass. A real world example I stumbled over was using a QObject from PyQt4. Then you have: class Spam(Registrar, QObject): pass which gives you a nice error message. Sure, you can now define a new metaclass: class MetaQRegistrar(MetaRegistrar, type(QObject)): pass but then you always have to write something like class Spam(Registrar, QObject, metaclass=MetaQRegistrar): pass everyone who uses Registrar needs to know the details of how those classes can be combined. With my addition, one could simply add a method to MetaRegistrar: def __add__(self, other): class Combination(self, other): pass return Combination which mixes MetaRegistrar into whatever comes. Sure, this is a very simplified example, one would cache the created classes, and maybe one wants to register classes that can be mixed. So, I hope this helps to illustrate my idea, now I'm answering specific questions raised:
Why the + operator? This doesn't seem to have anything to do with addition, or concatentation.
well, technically I'm fine with any method name, one of the binary operators just has the advantage that all the details with when __add__ as opposed to __radd__ is called are all sorted out already. But maybe the | operator is a better choice, as mixing classes is more like a union of two sets.
Your default implementation of __add__ shows that this has nothing to do with anything remotely like the standard use of plus:
That's actually not true. My methods combine classes, and if one is already a subclass of the other, they are already mixed, so yes, it is like a standard union of sets (I start to like the idea of |)
Just overriding __add__ (or whatever method used) doesn't solve the problem. You still have to actually make the metaclasses compatible, so that it is meaningful to mix the metaclasses.
Well, it does solve the problem. Before it was not at all possible to programmatically make metaclasses compatible, you had to write it down explicitly, each time of use.
They can be easily overwritten to get a different metaclass mixing behavior.
I question how easy that would be. If I have understood your proposal for metaclass __add__, mc1 + mc2 would have to return a new metaclass, mc3, which somehow combines behaviour from the two arguments. Even with cooperative metaclasses designed to work together, that sounds to me like it would be painful.
well, to give an example: class MetaClassMixin(type): def __add__(self, other): class ret(self, other): pass return ret just mixes your class into the other class. Greetings Martin
On 12 February 2015 at 05:42, Martin Teichmann
With my addition, one could simply add a method to MetaRegistrar:
def __add__(self, other): class Combination(self, other): pass return Combination
Having recently had to refactor some code in IPython which used multiple inheritance and metaclasses, -1 to this idea. 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). 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. Thomas
On 02/12/2015 05:42 AM, Martin Teichmann wrote:
class MetaQRegistrar(MetaRegistrar, type(QObject)): pass
but then you always have to write something like
class Spam(Registrar, QObject, metaclass=MetaQRegistrar): pass
or: class RQObject(metaclass=MetaQRegistrar): pass class Spam(RQObject): pass -- ~Ethan~
Create the required inheriting metaclass on the fly:
def multi_meta(*clss):
"""A helper for inheriting from classes with different metaclasses.
Usage:
class C(multi_meta(A, B)):
"A class inheriting from classes A, B with different
metaclasses."
"""
mcss = tuple(type(cls) for cls in clss)
mcs = type(
"Meta[{}]".format(", ".join(mcs.__name__ for mcs in mcss)), mcss,
{})
return mcs("[{}]".format(", ".join(cls.__name__ for cls in clss)),
clss, {})
I have the same use-case as you, i.e. mixing custom metaclasses with
QObjects.
2015-02-12 10:36 GMT-08:00 Ethan Furman
On 02/12/2015 05:42 AM, Martin Teichmann wrote:
class MetaQRegistrar(MetaRegistrar, type(QObject)): pass
but then you always have to write something like
class Spam(Registrar, QObject, metaclass=MetaQRegistrar): pass
or:
class RQObject(metaclass=MetaQRegistrar): pass
class Spam(RQObject): pass
-- ~Ethan~
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Hi Anthony, Hi list,
Create the required inheriting metaclass on the fly:
def multi_meta(*clss): """A helper for inheriting from classes with different metaclasses.
Usage: class C(multi_meta(A, B)): "A class inheriting from classes A, B with different metaclasses." """
That's actually exactly the code I am trying to avoid. This way, everyone starts writing their own metaclass mixers, which all make strong assumptions about the metaclasses being mixed. Now there should be only one way of doing things in python, unfortunately I'm not dutch so I don't see it. Maybe it would be a good idea to have such a mixer in the standard library. It would then cache already generated metaclasses. It would also ask the metaclasses on how they like to be mixed. Not before long, you will read recommendations like "just always write class A(mixer(B, C)), because well, you never now whether B or C have a metaclass". This is exactly why I would like to put it up one more level, make it part of type. Greetings Martin
participants (4)
-
Antony Lee
-
Ethan Furman
-
Martin Teichmann
-
Thomas Kluyver