Class Creation in Python

Learn how Python creates classes step by step: namespace preparation, metaclass invocation, __init_subclass__, and the full lifecycle of the class statement.

8 min read

Class creation in Python is the process that turns a class statement into a fully initialized class object. When Python encounters the class keyword, it does not simply store the body as a static definition. It executes code, builds a namespace, determines the metaclass, and calls multiple hooks before the class is ready to use.

The article on Python metaclasses covered how metaclasses control class creation from the outside. This article walks through the full creation lifecycle from the inside, showing each step in order and where you can hook into the process.

Understanding class creation helps you debug metaclass issues, implement class registries, and write frameworks that need to interact with class definitions at import time.

The step-by-step creation process

When Python processes a class statement, it follows a precise sequence. First, it extracts the class name, base classes, and keyword arguments from the statement header.

Next, it executes the class body in a new temporary namespace. Every assignment, function definition, and decorator call runs during this execution. The resulting namespace dictionary contains all the names defined in the body.

Python then determines the metaclass. If a metaclass keyword is present, that value is used; otherwise, Python finds the most derived metaclass among the base classes. If no base class has a metaclass, type is the default.

The metaclass new method is called with the class name, base tuple, and namespace dictionary. This method returns the new class object. Then the metaclass init method initializes the newly created class.

Finally, the init subclass method is called on the new class. This hook lets parent classes customize their subclasses after creation without requiring a metaclass.

Hooking into subclass creation with init subclass

The init subclass hook, available since Python 3.6, is the simplest way to react to new subclasses. Define it on a base class and Python calls it automatically whenever a subclass of that base is created.

pythonpython
class Base:
    subclasses = []
 
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        Base.subclasses.append(cls)
 
class User(Base):
    pass
 
class Product(Base):
    pass
 
for cls in Base.subclasses:
    print(cls.__name__)

Each subclass of Base is automatically registered in the subclasses list. No metaclass is required, and the subclasses themselves need no special syntax or decorators.

texttext
User
Product

The init subclass method receives keyword arguments passed in the class definition header. This lets parent classes expose configuration options to their subclasses.

pythonpython
class Tagged:
    def __init_subclass__(cls, tag=None, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.tag = tag
 
class Widget(Tagged, tag="ui"):
    pass
 
print(Widget.tag)

The tag keyword argument is consumed by Tagged init subclass method and stored as a class attribute on Widget. The subclass itself required no special code beyond passing the argument.

texttext
ui

Executing the class body as code

The class body is not a passive declaration. It is executed as code, which means you can put arbitrary logic inside it. This is how computed class attributes and conditional definitions work.

pythonpython
import sys
 
class Config:
    DEBUG = True
    LOG_LEVEL = "DEBUG" if DEBUG else "INFO"
 
    if sys.platform == "darwin":
        PLATFORM = "macos"
    elif sys.platform == "linux":
        PLATFORM = "linux"
    else:
        PLATFORM = "other"
 
print(Config.LOG_LEVEL)
print(Config.PLATFORM)

The body executes at definition time with full access to the enclosing scope. Conditional logic and computed values work just as they would in a function body.

texttext
DEBUG
macos

This code execution happens before the metaclass creates the class. By the time new runs, the namespace dictionary is fully populated with all the names defined during body execution.

Using metaclass new to validate the namespace

The metaclass new method receives the complete namespace dictionary before the class is created. This is the right place to validate that required methods exist, check naming conventions, or inject additional attributes.

pythonpython
class InterfaceMeta(type):
    def __new__(mcs, name, bases, namespace):
        if name != "Service":
            required = namespace.get("__required__", [])
            missing = [m for m in required if m not in namespace]
            if missing:
                raise TypeError(f"{name}: missing {missing}")
        return super().__new__(mcs, name, bases, namespace)
 
class Service(metaclass=InterfaceMeta):
    __required__ = ["start", "stop"]
 
class WebServer(Service):
    def start(self): return "started"
    def stop(self): return "stopped"
 
server = WebServer()
print(server.start())

The metaclass checks that every Service subclass defines start and stop. The validation happens at class definition time, failing fast with a clear error message.

texttext
started

The init subclass hook can also validate subclasses, but it runs after the class is created. The metaclass new hook runs before creation, which means the invalid class never comes into existence.

Combining all hooks in order

When you use all three hooks together, the execution order is predictable. The class body executes first, populating the namespace.

The metaclass new creates the class object. The metaclass init initializes it. The init subclass hook fires last.

This order lets you split responsibilities across hooks. Use the class body for attribute definition, metaclass new for validation and transformation before creation, metaclass init for post-creation setup that needs the fully formed class, and init subclass for parent-class reactions to new subclasses.

The article on Python namespace and scope internals covers how the namespace dictionary built during class creation relates to the broader namespace system Python uses everywhere else.

Rune AI

Rune AI

Key Insights

  • The class body is executed in a temporary namespace; all names defined become class attributes.
  • The metaclass is determined from the metaclass keyword, base class types, or defaults to type.
  • metaclass.new creates the class object; metaclass.init initializes it after creation.
  • init_subclass is called on the new class, giving parent classes a hook for subclass customization.
  • Use init_subclass for simple subclass registration; use metaclasses only when you need to control creation itself.
RunePowered by Rune AI

Frequently Asked Questions

What happens when Python executes a class statement?

Python first executes the class body in a temporary namespace, collecting all names defined there. It then determines the metaclass by looking for a metaclass keyword, the type of base classes, or defaulting to type. The metaclass.__new__ is called to create the class object, followed by metaclass.__init__. Finally, __init_subclass__ is called on the new class, allowing parent classes to customize subclasses after creation.

How is the metaclass for a class determined?

Python determines the metaclass in three steps. First, if the class definition includes a metaclass keyword argument, that metaclass is used. Second, if no metaclass is specified, Python looks at the metaclass of each base class. If all bases share a common metaclass, that metaclass is used. Third, if no metaclass can be determined, type is used as the default.

Conclusion

Class creation in Python is a multi-step process that you can hook into at several points. The namespace execution lets you compute class attributes dynamically. The metaclass new and init methods let you control and validate the class object itself. The init subclass hook lets parent classes react to new subclasses. Each hook serves a different purpose, and knowing all three gives you complete control over how your classes come into existence.