Decorators in Python
Decorators in Python are used to modify the behavior of a function or method. They are often used for logging, access control, memoization, and more. Decorators provide a clean and reusable way to extend the functionality of functions.
Defining a Decorator
A decorator is a function that takes another function as input and returns a new function.
def my_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
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Decorators with Arguments
You can create decorators that accept arguments by adding an extra layer of nesting.
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Class-Based Decorators
Decorators can also be implemented using classes. This is useful when you need to maintain state.
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
Chaining Decorators
You can apply multiple decorators to a single function. They are executed in the order they are listed.
def decorator1(func):
def wrapper():
print("Decorator 1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
Built-in Decorators
Python provides several built-in decorators, such as @staticmethod
, @classmethod
, and @property
.
class MyClass:
@staticmethod
def static_method():
print("This is a static method.")
@classmethod
def class_method(cls):
print(f"This is a class method of {cls.__name__}.")
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
obj = MyClass()
obj.static_method()
obj.class_method()
obj.name = "Alice"
print(obj.name)
Practical Use Cases
Decorators are commonly used for:
- Logging: Track function calls and their arguments.
- Timing: Measure the execution time of functions.
- Access Control: Restrict access to certain functions.
- Memoization: Cache results of expensive function calls.
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds to run.")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
print("Function executed.")
slow_function()
Back to Tutorial