Decorators in Python
Decorators are one of the most powerful features in Python. They allow you to modify or extend the behavior of functions without changing their code. This makes your programs cleaner, reusable, and easier to maintain.
What Is a Decorator?
A decorator is simply a function that takes another function as input, adds extra behavior, and returns a modified version of that function. Decorators are widely used in logging, authentication, performance tracking, and APIs.
Why Use Decorators?
They help avoid repeating code across multiple functions. They allow you to apply extra features without touching the original function logic. They improve structure and readability in larger applications.
Basic Decorator Structure
A decorator typically contains an inner function, also known as a wrapper. This wrapper function adds extra behavior before or after calling the original function. Below is the simplest example of a decorator.
def my_decorator(func):
def wrapper():
print("Before function runs")
func()
print("After function runs")
return wrapper
Using a Decorator
You apply a decorator using the @decorator_name syntax above the function.
This tells Python to wrap the function inside the decorator automatically.
This approach keeps your code neat and consistent.
@my_decorator
def greet():
print("Hello!")
greet()
Decorators with Arguments
Sometimes the function being decorated has its own arguments. In such cases, the wrapper function must accept those arguments as well. This pattern is common in validation, input checking, and logging.
def logger(func):
def wrapper(a, b):
print("Arguments:", a, b)
return func(a, b)
return wrapper
@logger
def add(x, y):
return x + y
print(add(5, 3))
Decorators Returning Values
A decorator can return the original function’s value. This makes decorators extremely flexible because they don’t interrupt normal function behavior. The wrapper simply adds extra functionality around the main return value.
def multiply_decorator(func):
def wrapper():
result = func()
return result * 2
return wrapper
@multiply_decorator
def number():
return 10
print(number())
Decorators for Access Control
Decorators are extremely useful for permission systems or user validation. You can check conditions before allowing a function to run. This approach keeps your main code clean and focused on logic.
def require_login(func):
def wrapper():
logged_in = True
if logged_in:
func()
else:
print("Access denied")
return wrapper
@require_login
def view_dashboard():
print("Dashboard opened")
view_dashboard()
Multiple Decorators
You can apply more than one decorator to a single function. Decorators run from top to bottom, wrapping the function layer by layer. This is useful in logging, caching, and authentication systems.
def one(func):
def wrapper():
print("Decorator 1")
func()
return wrapper
def two(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
@one
@two
def demo():
print("Running demo")
demo()
Real-World Uses of Decorators
They are used in web frameworks for route handling, authentication, and caching. They are used to measure performance and monitor API calls. They help automate repetitive tasks in clean and reusable ways.
📝 Practice Exercises
Exercise 1
Create a decorator that prints “Start” before a function runs and “End” after.
Exercise 2
Create a decorator that logs the name of the function being called.
Exercise 3
Create a decorator that checks if a number is positive before running a function.
Exercise 4
Create two decorators and apply both to one function to observe the order.
✅ Practice Answers
Answer 1
def border(func):
def wrapper():
print("Start")
func()
print("End")
return wrapper
@border
def say():
print("Hello")
say()
Answer 2
def log(func):
def wrapper():
print("Function called:", func.__name__)
func()
return wrapper
@log
def greet():
print("Hi there")
greet()
Answer 3
def check_positive(func):
def wrapper(n):
if n > 0:
func(n)
else:
print("Number must be positive")
return wrapper
@check_positive
def square(n):
print(n * n)
square(4)
Answer 4
def d1(func):
def wrapper():
print("Decorator 1")
func()
return wrapper
def d2(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
@d1
@d2
def hello():
print("Hello!")
hello()