Class Body Execution — Core Concepts

The Three Steps of Class Creation

When Python encounters a class statement, it performs three distinct operations:

  1. Prepare the namespace — Create a dictionary (or custom mapping) to hold the class body’s local variables.
  2. Execute the class body — Run the indented code as if it were a function body, with the namespace as the local scope.
  3. Call the metaclass — Pass the class name, bases, and populated namespace to the metaclass (usually type) to create the class object.

These steps happen at the point where the class statement appears in your code — not lazily, not deferred. It’s immediate.

Step 1: Namespace Preparation

Before executing the class body, Python calls metaclass.__prepare__(name, bases) to get the namespace dictionary. By default, this returns a regular dict.

Custom metaclasses can override __prepare__ to return a different mapping. This is how frameworks control what happens during class body execution — for example, returning an OrderedDict (pre-Python 3.7) or a custom mapping that tracks attribute access.

Step 2: Body Execution

The class body is compiled into a code object and executed with the prepared namespace as locals. This means:

  • Variable assignments create entries in the namespace.
  • Function definitions (including def and async def) add function objects.
  • Decorator applications happen to functions as they’re defined.
  • Any executable code runs immediately — loops, conditions, function calls.

What Runs and When

class Example:
    x = 10                    # Runs immediately — x added to namespace
    print(f"x is {x}")       # Runs immediately — prints "x is 10"

    def method(self):         # Function object created, added to namespace
        return self.x         # This body does NOT run yet

    y = x * 2                # Runs immediately — y is 20
    items = [i for i in range(3)]  # Runs immediately — [0, 1, 2]

The def method line creates a function object at class-creation time, but the function’s body only runs when someone calls method() later.

Step 3: The type() Call

After the body executes, Python calls:

ClassName = metaclass(name, bases, namespace)

For regular classes, this is type('Example', (object,), {...}). The type.__new__ method:

  1. Creates the class object.
  2. Copies the namespace into the class’s __dict__.
  3. Sets up the MRO.
  4. Calls __set_name__ on any descriptors.
  5. Calls __init_subclass__ on parent classes.

Timing: Import Time vs. Runtime

This distinction is critical for Python programs:

  • Import time: When a module is first imported, all top-level code runs — including all class statements. Class bodies execute during import.
  • Runtime: When methods are called on class instances.

This means side effects in class bodies (database connections, file I/O, network calls) happen at import time, which can cause problems in testing and deployment.

Common Misconception

“A class statement just defines a template.” In many languages (like Java or C#), a class declaration is purely declarative. In Python, it’s imperative — the body is executable code that runs at definition time. This is why you can use loops, conditionals, and function calls inside class bodies.

Practical Implications

Class Attributes Are Shared

class Counter:
    count = 0  # Runs once at class creation — shared by all instances

    def increment(self):
        Counter.count += 1  # Modifies the class attribute

All instances see the same count because it was set once during class body execution.

Mutable Default Gotcha

class Team:
    members = []  # Created once during body execution!

    def add(self, name):
        self.members.append(name)  # All instances share this list!

Since members = [] runs once at class creation, every Team instance shares the same list. Fix this by initializing in __init__.

Conditional Class Attributes

import sys

class Config:
    if sys.platform == "win32":
        path_separator = "\\"
    else:
        path_separator = "/"

The condition is evaluated once during class creation. The class gets only the matching attribute.

Decorator Order

Class decorators run after the class body executes and the class object is created:

@decorator    # Step 4: decorator(ClassName) runs last
class MyClass:
    x = 1     # Step 2: body executes
    pass
# Step 3: type() creates the class

The decorator receives the fully formed class object and can modify or replace it.

One thing to remember: A Python class statement is executable code. The body runs immediately at definition time, populating a namespace that becomes the class’s attributes. Understanding this timing is key to avoiding side-effect bugs and leveraging metaprogramming.

pythonadvancedoopinternals

See Also

  • Python Attribute Lookup Chain How Python finds your variables and methods — like checking your pockets, then your bag, then your locker, in a specific order every time.
  • Python Bytecode And Interpreter How your .py file turns into tiny instructions the Python interpreter can execute step by step.
  • Python Data Model Customization How Python lets your objects behave like built-in types — adding, comparing, looping, and printing, all with special methods.
  • Python Garbage Collection See how Python cleans up unreachable objects, especially the tricky ones that point at each other.
  • Python Gil Why Python threads can feel stuck in traffic, and how the GIL explains the behavior.