Order of __init_subclass__ and descriptor __set_name__

640 Views Asked by At

I have a number of dataclasses, all inheriting from a base class. mapping should be added dynamically to each of the subclasses and then used by the descriptor's __set_name__ to set a key inside it.

Here's what this structure looks like:

class Model:
    def __init_subclass__(cls, *args, **kwargs):
        cls.mapping = {}

@dataclass
class ModelA(Model):
    property: str = Descriptor()

class Descriptor:
    def __set_name__(self, owner, name):
        owner.mapping[name] = 'foobar'

It seems that mapping is not there when the descriptor's __set_name__ gets invoked. I tried using a metaclass for ModelA:

class Meta(type):
    def __new__(cls, name, bases, dict):
        dict['mapping'] = {}
        return super().__new__(cls, name, bases, dict)

however that still did not work.

What am I missing here with regards to how my hierarchy is built? And how can I achieve the load order I am after?

N.B. I am using Python 3.8.5

1

There are 1 best solutions below

1
On

__set_name__ happens before __init_subclass__. Here's where that's documented:

When using the default metaclass type, or any metaclass that ultimately calls type.__new__, the following additional customisation steps are invoked after creating the class object:

  • first, type.__new__ collects all of the descriptors in the class namespace that define a __set_name__() method;

  • second, all of these __set_name__ methods are called with the class being defined and the assigned name of that particular descriptor;

  • finally, the __init_subclass__() hook is called on the immediate parent of the new class in its method resolution order.