A Decorator is a powerful design pattern in Python that allows you to add additional functionality to a function without modifying its original code. Essentially, a decorator is a function that takes a function as a parameter and returns a new function.
Working Principle
1. Decorator Syntax Sugar
python@decorator def my_function(): pass
The code above is equivalent to:
pythonmy_function = decorator(my_function)
2. Simple Decorator Example
pythondef simple_decorator(func): def wrapper(): print("Before function execution") func() print("After function execution") return wrapper @simple_decorator def say_hello(): print("Hello, World!") say_hello()
Output:
shellBefore function execution Hello, World! After function execution
3. Decorator with Parameters
pythondef decorator_with_args(func): def wrapper(*args, **kwargs): print(f"Parameters: args={args}, kwargs={kwargs}") result = func(*args, **kwargs) return result return wrapper @decorator_with_args def add(a, b): return a + b print(add(3, 5)) # Output: Parameters: args=(3, 5), kwargs={} \n 8
4. Decorator with Custom Parameters
pythondef repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def greet(name): print(f"Hello, {name}!") greet("Python") # Will print three times
5. Using functools.wraps to Preserve Metadata
pythonfrom functools import wraps def logging_decorator(func): @wraps(func) def wrapper(*args, **kwargs): print(f"Calling function: {func.__name__}") return func(*args, **kwargs) return wrapper @logging_decorator def calculate(x, y): """Calculate the sum of two numbers""" return x + y print(calculate.__name__) # Output: calculate print(calculate.__doc__) # Output: Calculate the sum of two numbers
Practical Application Scenarios
1. Timer Decorator
pythonimport 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__} execution time: {end_time - start_time:.4f} seconds") return result return wrapper @timer def slow_function(): time.sleep(2) return "Completed" slow_function()
2. Cache Decorator
pythonfrom functools import wraps def memoize(func): cache = {} @wraps(func) def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper @memoize def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) print(fibonacci(100)) # Fast calculation
3. Permission Verification Decorator
pythonfrom functools import wraps def require_permission(permission): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): user_permissions = kwargs.get('permissions', []) if permission in user_permissions: return func(*args, **kwargs) else: raise PermissionError(f"Requires {permission} permission") return wrapper return decorator @require_permission('admin') def delete_user(user_id, permissions): print(f"Deleting user {user_id}") delete_user(1, permissions=['admin']) # Normal execution delete_user(1, permissions=['user']) # Throws permission error
Class Decorator
pythonclass CountCalls: def __init__(self, func): self.func = func self.count = 0 def __call__(self, *args, **kwargs): self.count += 1 print(f"Call count: {self.count}") return self.func(*args, **kwargs) @CountCalls def say_hi(): print("Hi!") say_hi() # Call count: 1 say_hi() # Call count: 2
Multiple Decorators Execution Order
pythondef decorator1(func): def wrapper(): print("Decorator 1 - Before") func() print("Decorator 1 - After") return wrapper def decorator2(func): def wrapper(): print("Decorator 2 - Before") func() print("Decorator 2 - After") return wrapper @decorator1 @decorator2 def my_function(): print("Function execution") my_function()
Output:
shellDecorator 1 - Before Decorator 2 - Before Function execution Decorator 2 - After Decorator 1 - After
Key Points
- Decorator Essence: A function that takes a function and returns a new function
- Syntax Sugar:
@decoratoris shorthand forfunc = decorator(func) - Parameter Passing: Use
*args, **kwargsto ensure decorators work for any function - Metadata Preservation: Use
@wraps(func)to preserve original function metadata - Execution Order: With multiple decorators, apply from bottom to top, execute from top to bottom
- Closures: Decorators use closure properties to access external variables
- Class Decorators: Implemented by defining the
__call__method
Decorators are an elegant way to implement cross-cutting concerns (such as logging, caching, permission verification) in Python, following the open-closed principle and making code more modular and maintainable.