CodeBlanks Logo Icon CodeBlanks

Parameterized decorator

Learn to write parameterized decorators.

Parameterized decorator

A decorator is a callable that wraps another function and changes its behavior. A parameterized decorator takes extra arguments that configure the wrapping.

You first define an outer factory function: def repeat(n):. That function returns the real decorator; the decorator returns the wrapped function. The three-layer shape is:

def repeat(n):
    def deco(fn):
        def wrapper(*args):
            for _ in range(n):
                fn(*args)
        return wrapper
    return deco

Use it as @repeat(3); the 3 argument goes to the factory.

Preserving the signature with functools.wraps

A decorator hides the wrapped function's name, docstring and signature; from the outside, wrapper is what gets called. This breaks debugging and help output.

The fix is to put functools.wraps(fn) on top of wrapper. It copies the original function's metadata onto wrapper.

from functools import wraps

def repeat(n):
    def deco(fn):
        @wraps(fn)
        def wrapper(*args):
            ...
        return wrapper
    return deco

That way help(my_fn) still shows the original docstring.

Practice Write a parameterized @repeat decorator

Write a three-layer parameterized decorator. The factory is repeat(n), the decorator is deco(fn) and the wrapper is wrapper(*args), nested. wrapper calls fn(*args) exactly n times. Preserve metadata with @wraps(fn). Decorate greet(name) with @repeat(2) so it prints the name value twice; end with a greet("Ayşe") call.

Starter code

main.py
from functools import wraps

# repeat(n) factory; define deco and wrapper nested inside
# decorate wrapper with the wraps decorator
# in the wrapper body call the original function n times

@repeat(2)
def greet(name):
    print(name)

greet("Ayşe")

Sample solution

main.py
from functools import wraps

def repeat(n):
    def deco(fn):
        @wraps(fn)
        def wrapper(*args):
            for _ in range(n):
                fn(*args)
        return wrapper
    return deco

@repeat(2)
def greet(name):
    print(name)

greet("Ayşe")

Expected output

Ayşe
Ayşe

Questions and answers

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

1 In a parameterized decorator, where does the 3 in @repeat(3) go?

  • To the factory function (repeat) Correct answer
  • Directly to the wrapped function
  • To the wrapper function
  • To the deco function

Explanation: repeat(3) calls the factory first; 3 binds to the n parameter of repeat.

2 Add the wraps decorator

Question
from functools import wraps

def repeat(n):
    def deco(fn):
        @____(fn)
        def wrapper(*args):
            for _ in range(n):
                fn(*args)
        return wrapper
    return deco
Answer
from functools import wraps

def repeat(n):
    def deco(fn):
        @wraps(fn)
        def wrapper(*args):
            for _ in range(n):
                fn(*args)
        return wrapper
    return deco

Explanation: @wraps(fn) copies the original function's name and docstring to wrapper.

3 Match the parameterized decorator layers

Term Meaning
repeat(n) Outer factory
deco(fn) Real decorator
wrapper(*args) Wrapping function
@wraps(fn) Preserves metadata

All lessons in Advanced