[Python-Dev] What is the design purpose of metaclasses vs code generating decorators? (was Re: PEP 557: Data Classes)

Martin Teichmann lkb.teichmann at gmail.com
Sat Oct 14 11:00:44 EDT 2017


>> I do worry that things like your autoslots decorator example might be
>> problematic because they create a new class, throwing away a lot of work
>> that was already done. But perhaps the right way to address this would be
>> to move the decision about the instance layout to a later phase? (Not sure
>> if that makes sense though.)
>>
>> --Guido
>
>
> Just FYI, recreating the class with slots runs into problems with regards to
> PEP 3135 (New Super). In attrs we resort to black magic to update the
> __class__ cell in existing methods. Being able to add slotness later would
> be good, but not that useful for us since we have to support down to 2.7.

You're both bringing up an important point here: while in function
decorators it is normal to return a completely new function (albeit
one that wraps the original), this is close to impossible for classes.
You cannot just wrap a class in another one. You may inherit from it,
but that's already often not what one wants.

While I am not worried about the poor computers having to do a lot of
work creating a throwaway class, I do see the problem with the new
super. What I would like to see is something like the @wraps decorator
for classes, such that you could write something like:

    def class_decorator(cls):
        @wraps_class
        class MyNewCoolClass:
              """my cool functionality here"""
         return MyNewCoolClass

wraps_class would then copy over everything such that the new class gets it.

Unfortunately, this won't work, because of the new super. The new
super is about the only thing that cannot be dynamically changed in
Python. While it is no problem to make a function a method of a random
class (just use __get__), it is not possible to move a function from
one class to another, because you cannot change its binding to
__class__, which is used by super(). And even if we could, the method
whose __class__ we want to change might hide in a wrapper.

The current behavior of __class__ is weird, it is set to the class
that type.__new__ creates. So even if another metaclasses __new__ or a
decorator returns another class, the method's __class__ would still
point to the original class, which might even not exist anymore.

One might argue that this is due to the fact that it is not
well-defined what __class__ should be set to. But this is not true, it
is crystal clear: __class__ should be set to the class from whose
__dict__ the method was drawn. I thought about a new three-parameter
__get__(self, instance, owner, supplier), which would then set
__class__ to the supplier. This is a beautiful concept, that is
unfortunately not so simple when it comes to methods hidden in
wrappers.

Greetings

Martin


More information about the Python-Dev mailing list