skip to content

__init_subclass__ and Subclass Hooks

__init_subclass__ lets a base run code every time it is subclassed — plugin registration, required-attribute checks — without a metaclass. Interviewers use it to see whether you reach for a metaclass too fast.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

4

When does Python call a base class's `__init_subclass__`, and what is `cls` bound to inside it?

level: middleimportance: must knowfreq 40%

answer

  1. A class-creation hook, not a metaclass
  2. Runs on the parent when subclassed
  3. cls is the brand-new subclass
  4. Implicit classmethod, no decorator needed
  5. Forward leftovers with super()

basics

~20 s

Python calls init_subclass on the nearest parent every time a new subclass of it is created, passing the brand-new subclass as cls. It is implicitly a classmethod, and it never runs for the class that defines it.

solid answer

~40 s

`__init_subclass__` is a class-creation hook added in Python 3.6 by PEP 487. While `type.__new__` builds a new class, it looks the hook up starting at the *parent* of that class and calls it with the freshly created subclass as `cls`, plus any keyword arguments from the class header. Python converts it to a classmethod implicitly, so you write `def __init_subclass__(cls, /, **kwargs)` with no `@classmethod` decorator. Because the lookup starts one step up the MRO, the hook does not fire for the class that defines it — only for descendants, and for descendants at any depth, not just direct children. Begin the body with `super().__init_subclass__(**kwargs)` so cooperating hooks further up still run; `object.__init_subclass__` accepts no keyword arguments, so consume yours first. It buys per-subclass registration or validation without a metaclass.

code

python · 17 lines
python
class Plugin:
    registry = {}

    def __init_subclass__(cls, /, **kwargs):
        super().__init_subclass__(**kwargs)
        Plugin.registry[cls.__name__] = cls


class Csv(Plugin):
    pass


class CsvStrict(Csv):
    pass


print(Plugin.registry)

go deeper

for a junior

Recall the one-line shape: a base class can define this hook and Python runs it whenever someone subclasses that base. Know that it is about class creation, not instance creation.

for a middle

Be ready to explain the mechanics: the hook is looked up on the parent, receives the new subclass as cls, is implicitly a classmethod, fires at every depth of the hierarchy, and must forward leftover keywords with super().

for a senior

Show judgment about the opt-out nature of the hook: it catches intermediates and grandchildren you did not plan for, so demonstrate how you guard registration and validation against that in a codebase other teams extend.

for a principal

Own the choice between this hook, a class decorator, an explicit registry and a metaclass, and be able to say what each costs a team in discoverability, debuggability and import-time coupling.

## What the hook is `__init_subclass__` arrived in Python 3.6 with PEP 487, "Simpler customisation of class creation". PEP 487 added two hooks — this one and `__set_name__` — with an explicit goal: remove the most common reason people reached for a metaclass, which was wanting to run a little code every time somebody subclassed a base class. Defining it is enough. There is no registration step, no custom metaclass, and no import-time magic: ```python class Plugin: registry = {} def __init_subclass__(cls, /, **kwargs): super().__init_subclass__(**kwargs) Plugin.registry[cls.__name__] = cls ``` ## Exactly when it runs Executing a class statement compiles the class body into a function, runs it to produce a namespace mapping, and hands name, bases and namespace to the metaclass — `type` unless you say otherwise. Inside `type.__new__` the class object is built first, and then, in this order, two hooks fire: every object in the *new* namespace that defines `__set_name__` is notified of its owner and attribute name, and only then is `__init_subclass__` called. The lookup is the part that matters. `type.__new__` does the equivalent of `super().__init_subclass__(cls=new_class, **class_keywords)` where the search starts at the class *after* the new one in its MRO. Two consequences follow directly: * The hook never runs for the class that defines it. When `Plugin` above is created, the search begins at `object`, and `object.__init_subclass__` is a do-nothing default. * It runs for descendants at any depth. A subclass of a subclass triggers it again, because the hook is inherited and the lookup finds it wherever it lives on the MRO. Interviewers probe this constantly, because code that assumes "direct children only" is a bug waiting for the first refactor that inserts an intermediate class. Both hooks run *inside* class creation, which means the class name is not yet bound in the enclosing module or function namespace when your hook body runs. You get the class object as `cls`; you cannot look it up by name yet. ## Why it is a classmethod without the decorator When a class body defines `__init_subclass__` as a plain function, class creation wraps it in `classmethod` for you. `__class_getitem__` gets the same implicit treatment; nothing else in the data model does. So the first parameter is the new subclass, not an instance. Adding `@classmethod` yourself is legal and harmless — the wrapping is idempotent — but it is not required, and an interviewer will read a `self` parameter there as a sign you have never actually used the hook. The conventional signature is `def __init_subclass__(cls, /, **kwargs)`. The `/` marks `cls` as positional-only, which frees the *name* `cls` for use as a class-header keyword; without it, `class C(Base, cls="x")` would collide with the first parameter. ## The cooperative super() call `object.__init_subclass__` accepts no keyword arguments at all: forwarding an unconsumed keyword to it raises `TypeError` at class-definition time. That is deliberate — a typo in a class header fails loudly at import rather than being silently ignored. The discipline is therefore: consume the keywords you own, forward the rest. ```python class Base: def __init_subclass__(cls, /, tag=None, **kwargs): super().__init_subclass__(**kwargs) cls.tag = tag ``` Skipping the `super()` call breaks cooperation: if two bases in a multiple-inheritance chain both define the hook, only the first one on the MRO runs, and the second silently stops working. Because the hook is looked up cooperatively, the same rules that govern any cooperative method chain apply here. ## What people actually use it for Four patterns dominate real code. **Registration**: the base keeps a mapping of subclasses so a factory can dispatch on a name. **Validation**: reject a subclass at import time if it forgot a required attribute or gave one the wrong type — `raise TypeError(...)` inside the hook makes the class statement itself fail. **Injection**: derive and attach an attribute, such as a slug computed from the class name. **Wrapping**: replace a method the subclass defined with an instrumented version. ## When something else is a better fit A class decorator registers exactly the class you decorate — opt-in, obvious in the source, and immune to inheritance surprises, at the price of being easy to forget. `__init_subclass__` is opt-out: it catches everything, including intermediates you did not mean to catch. A metaclass is still needed when you must control the namespace *before* the body executes or change how instances and subclass checks behave — but reaching for one merely to react to subclassing is the over-engineering that PEP 487 was written to retire.

  • Why does `__init_subclass__` not run for the class that defines it?
    Because class creation looks the hook up starting at the *parent* of the new class, not on the class itself. When the defining class is created, the search reaches `object.__init_subclass__`, which does nothing. If you also need the base processed, call the hook explicitly after the class statement, apply the same logic in a class decorator on the base, or move the work into a metaclass.
  • Which other data-model hook is implicitly converted to a classmethod the same way?
    `__class_getitem__`. Those two are the only members of the data model that class creation wraps in `classmethod` automatically; everything else, including `__new__` (a static method) and abstract-class hooks, needs its decorator written out. That is why a plain `def __class_getitem__(cls, item)` in a class body correctly receives the class when you subscript it.
  • Can the hook modify the class it receives?
    Yes. By the time it runs the class object is fully built — bases resolved, namespace populated, descriptor name callbacks already fired — so you can set attributes, wrap methods, or raise `TypeError` to make the class statement fail outright. What you cannot do is change the bases or the MRO; those were fixed when the class object was constructed.

It is a doorbell wired to the base class: every time someone builds a new room off it, the base gets to answer the door and inspect what was built.

saying these in an interview costs you the question

  • Says the hook also runs for the class that defines it
  • Thinks cls is the base class, not the new subclass
  • Writes self as the first parameter
  • Believes it only fires for direct subclasses
  • Omits the super() call and breaks cooperating bases
  • Claims only a metaclass can react to subclassing

context

open as a page

How do keyword arguments in `class C(Base, tag='x')` reach `__init_subclass__`?

level: middleimportance: should knowfreq 28%

basics

~20 s

Every keyword in a class header except metaclass is forwarded to the parent's init_subclass as a keyword argument, so a hook declared as def init_subclass(cls, /, tag=None, **kwargs) receives tag='x'. Consume what you own before calling super().

open as a page

Why can an `__init_subclass__` registry register the same handler twice, and how do you make it idempotent?

level: seniorimportance: should knowfreq 18%

basics

~20 s

Because the hook fires for every descendant at any depth, and because a module imported under two names defines two distinct class objects. Key the registry explicitly, skip intermediates with an opt-out header keyword, and raise on a duplicate key instead of appending.

open as a page

`__set_name__` or `__init_subclass__`: which runs first when Python creates a class?

level: seniorimportance: nice to knowfreq 12%

basics

~20 s

set_name runs first. Class creation builds the class object, notifies every attribute in the new namespace that defines set_name, and only then calls the parent's init_subclass — so the hook already sees attributes that know their own names.

open as a page