__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 when | Instance is created | Subclass is defined |
| First parameter | self (the instance) | cls (the subclass) |
| Runs how many times? | Once per instance | Once per subclass definition |
| Typical purpose | Initialise instance state | Register 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 toobject.__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
classstatement does. objectdefault: The rootobjectclass provides a no-op default that terminates the cooperative chain:
class object:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)