A decorator is a function that takes another function and returns a new function that adds some behaviour around it. The @ line applies it.
The program
python
import functools def announce(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f"Calling {func.__name__}{args}") result = func(*args, **kwargs) print(f"{func.__name__} returned {result}") return result return wrapper @announce def add(a, b): return a + b total = add(3, 4) print("total is", total)
setup added so this can run · defines args, kwargs
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) args = _AutoMock('args') kwargs = _AutoMock('kwargs')
output
Calling add(3, 4) add returned 7 total is 7
How it works
@announceabovedef addmeans the same as writingadd = announce(add)after the function. From then on, the nameaddrefers towrapper.wrappertakes*args, **kwargs, so it accepts any arguments and passes them on to the original function unchanged.wrappermustreturn result. If it does not, every decorated function returnsNone.@functools.wraps(func)copies the name and docstring of the original function ontowrapper. Without it,add.__name__would be"wrapper", which makes error messages and debugging harder.
A timing decorator
The same pattern measures how long a function takes. time.perf_counter() is the clock to use for measuring short durations:
python
import functools import time def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) elapsed = time.perf_counter() - start print(f"{func.__name__} took {elapsed:.4f} seconds") return result return wrapper @timer def slow_sum(n): return sum(range(n)) print(slow_sum(1_000_000))
setup added so this can run · defines args, kwargs
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) args = _AutoMock('args') kwargs = _AutoMock('kwargs')
The time is different on every computer and every run, so this page does not show its output. You will see a line like slow_sum took 0.0150 seconds, followed by the sum, 499999500000.
Related programs: Logging decorator, Retry decorator, Closure.