Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Python __init_subclass__ and Class Creation Hooks

Learn how Python bases can configure, validate, and register subclasses with __init_subclass__.

Levels: Intermediate ~20 min read

Intermediate

Python gives a class a hook named __init_subclass__ that runs when a subclass is created. It lets a base class configure or validate future subclasses without requiring a custom metaclass.

Run code when a subclass is created

Define __init_subclass__ on a base class to run code whenever a direct or indirect subclass is created. The cls parameter refers to the new subclass, not to the base class that defines the hook.

python
1
2
3
4
5
6
7
8
class Base:
    def __init_subclass__(cls):
        print("created:", cls.__name__)

class Report(Base):
    pass

print(Report.__name__)

Creating Report calls Base.__init_subclass__ with Report as cls.

The hook receives the new class

__init_subclass__ is defined like a normal method, but Python arranges for it to behave as a class method during subclass creation. Use cls to inspect or configure the new subclass.

python
1
2
3
4
5
6
7
8
class Base:
    def __init_subclass__(cls):
        cls.ready = True

class Service(Base):
    pass

print(Service.ready)

The hook adds a class attribute to the newly created subclass.

Accept class-definition options

A class definition can pass keyword arguments that a base class's __init_subclass__ accepts. This is useful when subclasses declare small pieces of configuration next to their class header.

python
1
2
3
4
5
6
7
8
class Plugin:
    def __init_subclass__(cls, /, *, kind):
        cls.kind = kind

class CachePlugin(Plugin, kind="cache"):
    pass

print(CachePlugin.kind)

kind is supplied in the subclass header and stored on the new class.

Forward unused keywords with super()

A cooperative __init_subclass__ should consume the keywords it owns and forward the rest to super().__init_subclass__. This lets multiple classes participate in subclass customization without each class having to know every option.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Base:
    def __init_subclass__(cls, /, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.base_ready = True

class Named(Base):
    def __init_subclass__(cls, /, *, label, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.label = label

class Report(Named, label="report"):
    pass

print(Report.base_ready, Report.label)

Named consumes label and forwards any remaining subclass keywords.

Validate subclasses at definition time

Because the hook runs while the subclass is created, it can reject an invalid subclass immediately. Raising an exception stops that class definition from succeeding.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Command:
    def __init_subclass__(cls, /, **kwargs):
        super().__init_subclass__(**kwargs)
        if not hasattr(cls, "run"):
            raise TypeError(f"{cls.__name__} must define run()")

class Start(Command):
    def run(self):
        return "start"

print(Start().run())

Start is created successfully because it defines the required run method.

Build a small subclass registry

A base class can use __init_subclass__ to register each new subclass. The registry is just ordinary class data; the hook supplies the moment when a subclass enters it.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Handler:
    registry = []

    def __init_subclass__(cls, /, **kwargs):
        super().__init_subclass__(**kwargs)
        Handler.registry.append(cls)

class JsonHandler(Handler):
    pass

class TextHandler(Handler):
    pass

print([cls.__name__ for cls in Handler.registry])

Each subclass is added to Handler.registry when its class definition completes.

Subclasses of subclasses also trigger the inherited hook

A subclass inherits __init_subclass__ unless it overrides it, so creating a deeper subclass can trigger the hook again. The cls value identifies the newly created class each time.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Base:
    created = []

    def __init_subclass__(cls):
        Base.created.append(cls.__name__)

class First(Base):
    pass

class Second(First):
    pass

print(Base.created)

The inherited hook records both First and its subclass Second.

Know where __init_subclass__ fits

__init_subclass__ is a class-creation hook for configuring future subclasses. It is different from a class decorator, which transforms one particular class after it is created, and from a metaclass, which controls the class-creation machinery itself.

Keep the hook small and explicit: consume the options your base class owns, pass unrelated options to super().__init_subclass__, and let the subclass definition remain readable.

Key takeaways

  • __init_subclass__ runs when a subclass is created, and cls is the new subclass.
  • A base class can configure, validate, or register subclasses through the hook.
  • Subclass keyword arguments can configure the hook; cooperative hooks should forward unused keywords with super().
  • __init_subclass__ is a lighter class-creation customization mechanism than a custom metaclass.
Was this page helpful?

Mark this learning resource complete to track your learning progress.