Decorators (Function & Class)
Decorators wrap functions to add behavior without modifying the original function. They're used for logging, timing, authentication, caching, and more.
20 min•By Priygop Team•Updated 2026
Decorators
Decorators
import functools
import time
# Basic decorator
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start
print(f" {func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
total = sum(range(1_000_000))
return total
result = slow_function()
# Decorator with arguments
def repeat(n):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f" Hello, {name}!")
greet("Alice")
# Practical: logging decorator
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f" Calling {func.__name__}({args}, {kwargs})")
result = func(*args, **kwargs)
print(f" {func.__name__} returned {result}")
return result
return wrapper
@log
def add(a, b):
return a + b
add(5, 3)
# Built-in decorators
# @property, @classmethod, @staticmethod
# @functools.lru_cache — memoization
@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n < 2: return n
return fibonacci(n-1) + fibonacci(n-2)
print(f"\nfib(30) = {fibonacci(30)}")Tip
Tip
@functools.lru_cache(maxsize=128) is the easiest way to add memoization. Use @functools.wraps in custom decorators to preserve metadata.
Diagram
Loading diagram…
@decorator = syntactic sugar for fn = decorator(fn). Use functools.wraps to preserve metadata.
Common Mistake
Warning
Decorators replace the original function. Without @functools.wraps, the decorated function loses its __name__ and __doc__.
Quick Quiz
Practice Task
Note
(1) Write a @timer decorator. (2) Write a @retry decorator. (3) Stack two decorators on one function.