Decorators in Python
Decorators in Python
A decorator is a function that modifies the behavior of another function without permanently modifying it. Decorators are a powerful tool that use closure functions.
Basic Concept
A decorator:
- Takes a function as input
- Returns a modified function (or a new function)
- Uses the
@decorator_namesyntax
Simple Example
python
def simple_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
# Using the decorated function
say_hello()
Output:
text
Something is happening before the function is called. Hello! Something is happening after the function is called.
Equivalent Without @ Syntax
python
def say_hello():
print("Hello!")
# Manual decoration
decorated_hello = simple_decorator(say_hello)
decorated_hello()
Decorator for Functions with Arguments
python
def smart_divide(func):
def wrapper(a, b):
print(f"Dividing {a} by {b}")
if b == 0:
print("Cannot divide by zero!")
return
return func(a, b)
return wrapper
@smart_divide
def divide(a, b):
return a / b
print(divide(10, 2)) # Output: Dividing 10 by 2 → 5.0
print(divide(10, 0)) # Output: Dividing 10 by 0 → Cannot divide by zero!
Decorator with Any Number of Arguments
python
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned: {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
@logger
def multiply(x, y, z=1):
return x * y * z
add(3, 5)
multiply(2, 3, z=4)
Output:
text
Calling add with args: (3, 5), kwargs: {}
add returned: 8
Calling multiply with args: (2, 3), kwargs: {'z': 4}
multiply returned: 24
Practical Example: Timing Function Execution
python
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
return "Done!"
slow_function() # Output: slow_function executed in 2.0002 seconds
Chaining Decorators
python
def bold(func):
def wrapper():
return "<b>" + func() + "</b>"
return wrapper
def italic(func):
def wrapper():
return "<i>" + func() + "</i>"
return wrapper
@bold
@italic
def hello():
return "Hello World"
print(hello()) # Output: <b><i>Hello World</i></b>
Decorator with Arguments
python
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n):
print(f"Call {i+1}:")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
text
Call 1: Hello, Alice! Call 2: Hello, Alice! Call 3: Hello, Alice!
Why Use Decorators?
- Code Reuse: Avoid repetitive code
- Separation of Concerns: Keep business logic separate from cross-cutting concerns
- Readability: Makes code more readable and maintainable
- Extensibility: Easy to add/remove functionality
Decorators are widely used in web frameworks (like Flask, Django), testing, logging, and many other areas of Python programming!