Python
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.
Exercise 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.
Show example solution
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")
Test yourself
-
In a parameterized decorator, where does the 3 in @repeat(3) go?
- To the factory function (repeat)
- Directly to the wrapped function
- To the wrapper function
- To the deco function
Show answer
To the factory function (repeat) — repeat(3) calls the factory first; 3 binds to the n parameter of repeat.
-
Add the wraps decorator
from functools import wraps
def repeat(n):
def deco(fn):
@____(fn)
def wrapper(*args):
for _ in range(n):
fn(*args)
return wrapper
return deco
Show 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
-
Match the parameterized decorator layers
- repeat(n)
- Outer factory
- deco(fn)
- Real decorator
- wrapper(*args)
- Wrapping function
- @wraps(fn)
- Preserves metadata
Next lesson: Class decorator