🌟 Python Decorators: A Function That Takes Another Function As Input
A decorator in Python is simply a way to modify or enhance functions (or methods, or even classes) without changing their actual code. Think of them like wrapping a gift: the gift (your function) stays the same inside, but the wrapping (the decorator) changes how it looks, behaves, or what happens when it’s opened.
They are built on two key concepts:
- Functions are first-class objects in Python (they can be passed around as arguments, returned from other functions, stored in variables, etc.).
- Nested functions & closures (a function defined inside another function can “remember” values from the enclosing scope).
đź§© How Do They Work?
A decorator is a function that takes another function as input, adds some extra behavior, and returns a new function.
Let’s see a simple example:
def decorator(func):
def wrapper():
print("Something before the function runs...")
func()
print("Something after the function runs...")
return wrapper
Here’s how we use it:
@decorator
def say_hello():
print("Hello!")
say_hello()
Output:
Something before the function runs...
Hello!
Something after the function runs...
The @decorator
is shorthand for:
say_hello = decorator(say_hello)
⚡ Real-Life Analogy
Imagine you run a bakery. You bake bread (your function). But before selling, you might want to:
- Add packaging (logging).
- Add a label (authentication).
- Add a discount coupon (caching).
The bread itself hasn’t changed—it’s the wrapping that gives extra features. That’s what decorators do.
🛠️ Common Use Cases of Decorators
-
Logging – Keeping track of function calls.
def log_decorator(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with {args} {kwargs}") return func(*args, **kwargs) return wrapper
-
Authentication / Permission checks – Useful in web frameworks.
def require_admin(func): def wrapper(user, *args, **kwargs): if not user.is_admin: raise PermissionError("Admin access required") return func(user, *args, **kwargs) return wrapper
-
Caching / Memoization – Avoid repeating expensive computations.
from functools import lru_cache @lru_cache(maxsize=1000) def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2)
-
Timing functions – Measuring performance.
import time def timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.2f} seconds") return result return wrapper
🎯 Key Points to Remember
- Decorators use
@decorator_name
syntax. - They are just functions returning functions.
-
functools.wraps
is often used inside decorators to preserve the original function’s metadata (like name, docstring).from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
đź”® Beyond Basics
-
Decorators can also accept arguments by nesting another function.
def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator @repeat(3) def greet(): print("Hi!") greet()
Output:
Hi! Hi! Hi!
-
You can also apply multiple decorators to the same function, and they stack from top to bottom.
âś… In short: Decorators are powerful, reusable, elegant tools for modifying functions without touching their code. They are widely used in frameworks like Flask, Django, FastAPI, and in Python libraries for logging, authentication, caching, etc.
✍ Review Fill-in-the-Gap Questions
- A decorator in Python is a __ that takes another function and returns a __.
- The
@decorator_name
syntax is just shorthand for __. - Functions in Python are called __ objects because they can be passed around like data.
- A decorator often defines an inner function called __ to wrap extra behavior.
- To preserve the original function’s name and docstring, we use __ from the functools module.
- Decorators can be stacked; they are applied from __ to __.
- One common use of decorators is adding __, which helps track when functions are called.
- The
@lru_cache
decorator is useful for __ expensive function results. - A decorator with parameters requires an additional __ layer of function nesting.
- In web frameworks, decorators are often used for checking __ and permissions.