What is a decorator and how it works?
- Python’s decorators allow you to extend and modify the behavior of a callable (functions, methods, and classes) without permanently modifying the callable itself.
- Decorators should use the
@functools.wrapsdecorator, which will preserve information about the original function. Preserves original name of the function.
# Functions returning Functions
def f(x):
def g(y):
return y + x + 3
return g
nf1 = f(1) # func = decorator(func)
nf2 = f(3)
print(nf1(1)) # 5
print(nf2(1)) # 7‣
Decorator examples
‣
‣
‣
@repeat(num_times=4)Advanced decorator examples
‣
‣
GitHub examples
# EXAMPLE 1: Simple example without argument
import functools
def require_authorization(f):
@functools.wraps(f)
def decorated(user, *args, **kwargs):
if not is_authorized(user):
raise UserIsNotAuthorized
return f(user, *args, **kwargs)
return decorated
@require_authorization
def check_email(user, etc):
# etc.# EXAMPLE 2: Decorator factory (passing args to decorators)
def require_authorization(action):
def decorate(f):
@functools.wraps(f):
def decorated(user, *args, **kwargs):
if not is_allowed_to(user, action):
raise UserIsNotAuthorized(action, user)
return f(user, *args, **kwargs)
return decorated
return decorateUse cases
‣
‣
Ways a Decorator Modifies Behavior
A decorator can change a function in several powerful ways:
- Pre-processing: Checking if a user is logged in before allowing the function to run (Authentication).
- Post-processing: Converting a function's return value into a different format (like JSON or HTML).
- Rate Limiting: Checking how many times a function has been called and stopping it if it’s too frequent.
- Caching (Memoization): Checking if the function has been called with these specific arguments before. If it has, the decorator returns the saved result instead of running the function again.
- Error Handling: Wrapping the function in a
try...exceptblock to log errors globally.
Why is this better than just editing the function?
- Don't Repeat Yourself (DRY): If you have 50 functions that all need logging, you only write the logging logic once in the decorator.
- Separation of Concerns: for example the
add_numbersfunction should only care about math. It shouldn't have to care about logging, security, or database connections. The decorator handles the "plumbing," while the function handles the "logic."