pythonclasssubclasspython-3.6metaclass

Understanding __init_subclass__


I finally upgraded my python version and I was discovering the new features added. Among other things, I was scratching my head around the new __init_subclass__ method. From the docs:

This method is called whenever the containing class is subclassed. cls is then the new subclass. If defined as a normal instance method, this method is implicitly converted to a class method.

So I started to playing around with it a little bit, following the example in the docs:

class Philosopher:
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        print(f"Called __init_subclass({cls}, {default_name})")
        cls.default_name = default_name

class AustralianPhilosopher(Philosopher, default_name="Bruce"):
    pass

class GermanPhilosopher(Philosopher, default_name="Nietzsche"):
    default_name = "Hegel"
    print("Set name to Hegel")

Bruce = AustralianPhilosopher()
Mistery = GermanPhilosopher()
print(Bruce.default_name)
print(Mistery.default_name)

Produces this output:

Called __init_subclass(<class '__main__.AustralianPhilosopher'>, 'Bruce')
'Set name to Hegel'
Called __init_subclass(<class '__main__.GermanPhilosopher'>, 'Nietzsche')
'Bruce'
'Nietzsche'

I understand that this method is called after the subclass definition, but my questions are particularly about the usage of this feature. I read the PEP 487 article as well, but didn't help me much. Where would this method be helpful? Is it for:

  • the superclass to register the subclasses upon creation?
  • forcing the subclass to set a field at definition time?

Also, do I need to understand the __set_name__ to fully comprehend its usage?


Solution

  • __init_subclass__ and __set_name__ are orthogonal mechanisms - they're not tied to each other, just described in the same PEP. Both are features that needed a full-featured metaclass before. The PEP 487 addresses two of the most common uses of metaclasses:

    • how to let the parent know when it is being subclassed (__init_subclass__)
    • how to let a descriptor class know the name of the property it is used for (__set_name__)

    As PEP 487 says:

    While there are many possible ways to use a metaclass, the vast majority of use cases falls into just three categories: some initialization code running after class creation, the initialization of descriptors and keeping the order in which class attributes were defined.

    The first two categories can easily be achieved by having simple hooks into the class creation:

    • An __init_subclass__ hook that initializes all subclasses of a given class.
    • upon class creation, a __set_name__ hook is called on all the attribute (descriptors) defined in the class, and

    The third category is the topic of another PEP, PEP 520.

    Notice also, that while __init_subclass__ is a replacement for using a metaclass in this class's inheritance tree, __set_name__ in a descriptor class is a replacement for using a metaclass for the class that has an instance of the descriptor as an attribute.