Class decorators

RoadmapsPython

Overview

A class decorator is a function that takes a class object as an argument and returns a modified class. Instead of modifying instances, it alters the class definition itself. The class name is rebound to whatever the decorator returns, applied once at definition time. Think of it like a car factory adding a custom spoiler to every car model before any cars are actually produced. When writing a class decorator, you can: - Mutate the class directly to add attributes or methods - Register the class in a global registry - Return an entirely new wrapper class Returning a wrapper class creates an identity trap that hides the original class metadata (like `__name__` and `__doc__`). def wrap(cls): class Wrapper: pass return Wrapper @wrap class Job: pass print(Job.__name__) # Prints 'Wrapper'

It reduces boilerplate when you need to apply the same structural changes across many classes. Common use cases include: - Adding standard methods like `__init__` or `__repr__` - Registering classes to a central store or registry - Enforcing class-level constraints

Where used: `dataclasses` (`@dataclass`), `pytest` (`@pytest.mark`), `SQLAlchemy`

Why learn this

Code walkthrough

def add_id(cls):
  cls.id_field = 'default_id'
  return cls

@add_id
class User:
  pass

print(User.id_field)

Focus: cls.id_field = 'default_id' # The class object is mutated directly

Aha moment

def oops_decorator(cls):
  print('Decorated!')
  # forgot to return cls

@oops_decorator
class App:
  pass

print(type(App))

Prediction: What is the type of `App`?

Common guess: `<class 'type'>`

Because `oops_decorator` didn't return anything, it implicitly returned `None`. The name `App` is now bound to `None`, not the class.

Common mistakes

Glossary

boilerplate
Standard pieces of code that have to be written over and over again with little alteration, e.g., `def __init__(self, x): self.x = x`.
mutate
To change or alter the internal state or data of an object, e.g., `user.age += 1`.

Recall questions

Understanding checks

What does this code print?

20

The class decorator receives the class object, modifies its `value` attribute directly to `20`, and returns it.

Why would you choose a class decorator over modifying instances directly in the `__init__` method?

Class decorators alter the class definition itself once (e.g., adding standard boilerplate or registering the class globally), rather than running logic every time a new instance is created.

This reinforces the mental model that a class decorator modifies the blueprint, not the individual objects produced from it.

Practice tasks

Modify Plugin Registration

The `register_plugin` decorator currently appends the class name to `PLUGINS`. Modify it so that it appends the actual class object to `PLUGINS` instead, and ensure it returns the `cls` object.

Challenge

Adding a class-level timestamp

Create a class decorator `add_timestamp` that adds a class attribute `created_at` to the decorated class. Set its value to `'2023-01-01'`. Apply this decorator to a class `Record` and print `Record.created_at`.

Continue learning

Previous: Function Decorators

Previous: Everything is an object in Python

Return to Python Roadmap