pyguides

__init_subclass__

__init_subclass__(cls, /, **kwargs)

Python calls __init_subclass__ automatically whenever a class inherits from a parent that defines the method. It fires at class definition time — the moment the class statement executes — not when you instantiate the class. You do not call it manually, and you do not declare it as @classmethod; Python handles that implicitly.

Added in Python 3.6 via PEP 487 as a simpler alternative to metaclasses for the most common subclass registration and configuration use cases.

Signature and Parameters

def __init_subclass__(cls, /, **kwargs):
    super().__init_subclass__(**kwargs)
  • cls — the newly created subclass. Python passes this automatically when the class is defined.
  • **kwargs — any keyword arguments passed in the subclass definition are forwarded here.
  • Return type: None.

The positional-only / marker means cls cannot be passed as a keyword argument. The **kwargs forwarding is essential for cooperative multiple inheritance to work correctly.

When Is It Called?

__init_subclass__ runs once per subclass definition, on every direct parent that defines it, in method resolution order (MRO) order.

class Parent:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        print(f"Registering: {cls.__name__}")

class Child(Parent):
    pass

class Grandchild(Child):
    pass

This prints Registering: Child when Child is defined. Grandchild does not trigger the hook again, because Child does not define __init_subclass__ — the call falls through to Parent only for direct subclasses.

Crucially, instantiation does not call __init_subclass__. Only the class statement triggers it:

Child()        # creates an instance — __init_subclass__ is NOT called here
class Another(Parent):  # __init_subclass__ fires here
    pass

__init_subclass__ vs __init__

These names are similar but serve entirely different purposes.

__init____init_subclass__
Called whenInstance is createdSubclass is defined
First parameterself (the instance)cls (the subclass)
Runs how many times?Once per instanceOnce per subclass definition
Typical purposeInitialise instance stateRegister or configure subclasses
class Base:
    def __init__(self):
        print("__init__ called")

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        print(f"__init_subclass__ called for {cls.__name__}")

class Derived(Base):
    pass
# Output:
# __init_subclass__ called for Derived

Derived()
# __init__ called

__init_subclass__ vs Metaclass __init__

Both run at class creation time, but on different objects:

# Metaclass approach
class RegistryMeta(type):
    def __init__(cls, name, bases, ns):
        super().__init__(name, bases, ns)
        RegistryMeta._registry.append(cls)

class Registry(metaclass=RegistryMeta):
    _registry = []

# __init_subclass__ equivalent
class Registry:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Registry._registry.append(cls)

Metaclass __init__ operates on the metaclass itself. __init_subclass__ operates on the newly created class. The hook was designed to cover the most common metaclass use cases — subclass registries, validation, plugin systems — without the conflict risk that comes with full metaclass machinery.

Cooperative Inheritance

If your base class defines __init_subclass__ and you expect it to be subclassed in a multiple-inheritance setup, you must forward **kwargs through super():

class Base:
    def __init_subclass__(cls, register=True, **kwargs):
        super().__init_subclass__(**kwargs)  # required for cooperative chain
        if register:
            Base._all.append(cls)

class Middle(Base, register=False):
    pass

Omitting super().__init_subclass__() breaks the chain. Parent classes further up the MRO will never run, which can cause silent failures in complex inheritance hierarchies.

Keyword Arguments at Definition Time

When you define a subclass, any keyword arguments in the class header are forwarded to __init_subclass__ on the parent. You must explicitly declare **kwargs to receive them:

class PluginBase:
    def __init_subclass__(cls, plugin_name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if plugin_name:
            cls._plugin_name = plugin_name

class MyPlugin(PluginBase, plugin_name="my-plugin"):
    pass

print(MyPlugin._plugin_name)  # "my-plugin"

Preventing Subclass Creation

__init_subclass__ can abort class creation by raising TypeError. This is useful for enforcing invariants on anything that subclasses your base:

class RequireAttr:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, 'required_attr'):
            raise TypeError(
                f"Cannot subclass {cls.__name__} without a 'required_attr' attribute"
            )

class Valid(RequireAttr):
    required_attr = True  # passes

class Invalid(RequireAttr):  # TypeError raised here
    pass

The error fires at class definition time, which means invalid subclasses never make it into your codebase.

Common Patterns

Class Registry (Plugin System)

class PluginRegistry:
    _plugins = {}

    def __init_subclass__(cls, name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        plugin_name = name or cls.__name__
        cls._plugins[plugin_name] = cls

    @classmethod
    def get(cls, name):
        return cls._plugins.get(name)

class TextPlugin(PluginRegistry, name="text"):
    pass

class ImagePlugin(PluginRegistry, name="image"):
    pass

print(PluginRegistry.get("text"))  # <class 'TextPlugin'>

Automatic Attribute Validation

class Validated:
    _required = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        for attr in cls._required:
            if attr not in cls.__dict__:
                raise TypeError(f"{cls.__name__} must define '{attr}'")

class Point(Validated):
    _required = ['x', 'y']
    def __init__(self, x, y):
        self.x, self.y = x, y

Edge Cases

  • No __init_subclass__ defined: Python falls back to object.__init_subclass__, which is a no-op. No error.
  • Metaclass + __init_subclass__: Both run. The metaclass __new__/__init__ executes first, then __init_subclass__ on the class’s bases.
  • Not triggered by assignment: Assigning a class to a variable does not fire the hook. Only the class statement does.
  • object default: The root object class provides a no-op default that terminates the cooperative chain:
class object:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)

See Also

  • __call__ — called when an instance is invoked as a function
  • __new__ — creates and returns a new instance before __init__
  • __str__ — defines the human-readable string representation of an object