CodeBlanks Logo Icon CodeBlanks

Class decorator

Learn class-based decorators.

Class decorator

A class decorator makes a class callable through the __call__ method and uses that class as a decorator instead of a function. The instance behaves like a function.

The advantage: it can hold state while wrapping. For example, it can count how many times it was called in an instance attribute. The shape is:

class Counter:
    def __init__(self, fn):
        self.fn = fn
        self.count = 0
    def __call__(self, *args):
        self.count += 1
        return self.fn(*args)

A function decorated with @Counter is now a Counter instance; fn.count gives the call count.

Tracking state with a class decorator

The strength of class decorators is that they can hold state. You keep call counts, caches or timestamps in instance attributes.

Example: each call increments a counter and returns the result. If square(x) decorated with @Counter is called three times, square.count becomes 3.

Inside, self.fn(*args) runs the original function; self.count += 1 updates the counter. State stays on the instance; no global variable is needed.

Practice Call counter with a class decorator

Write a class decorator named Counter. Its __init__(self, fn) stores the function on self.fn and sets self.count to 0. Its __call__(self, *args) increments self.count each call and returns self.fn(*args). Decorate square(x) with @Counter so it returns x * x. Call square(3), square(4) and square(5); end with print(square.count).

Starter code

main.py
# Counter: class decorator with __init__ and __call__
# __init__ should hold fn and the count value
# __call__ should bump count and call fn

@Counter
def square(x):
    return x * x

square(3)
square(4)
square(5)
print(square.count)

Sample solution

main.py
class Counter:
    def __init__(self, fn):
        self.fn = fn
        self.count = 0

    def __call__(self, *args):
        self.count += 1
        return self.fn(*args)

@Counter
def square(x):
    return x * x

square(3)
square(4)
square(5)
print(square.count)

Expected output

3

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 The call method in a class decorator

Question
class Counter:
    def __init__(self, fn):
        self.fn = fn
        self.count = 0

    def ____(self, *args):
        self.count += 1
        return self.fn(*args)
Answer
class Counter:
    def __init__(self, fn):
        self.fn = fn
        self.count = 0

    def __call__(self, *args):
        self.count += 1
        return self.fn(*args)

Explanation: The __call__ method makes an instance callable; that is what gives the decorator behavior.

2 Where does a class decorator store call info?

  • In a global variable
  • In an instance attribute (like self.count) Correct answer
  • In a local variable
  • In a module-level constant

Explanation: A class decorator stores state in self-prefixed instance attributes; each instance has its own counter.

3 Match the class decorator pieces

Term Meaning
__init__(self, fn) Holds the original function
__call__(self, *args) Runs when called
self.count State attribute
self.fn(*args) Calls the original function

All lessons in Advanced