Decorators in Python
Description:
Decorators are a powerful syntactic feature in Python that allow you to dynamically enhance the functionality of a function without modifying its original code. They are essentially higher-order functions that accept a function as an argument and return a new function. Decorators are commonly used in scenarios such as logging, performance testing, and permission verification.
Problem-Solving Process:
-
Basic Concept: Functions as Objects
In Python, functions are first-class objects, meaning they can be passed, assigned, or used as arguments/return values of other functions, just like variables. For example:def greet(name): return f"Hello, {name}!" # Assigning a function to a variable my_func = greet print(my_func("Alice")) # Output: Hello, Alice! -
Higher-Order Functions: Functions as Arguments or Return Values
The core of decorators is higher-order functions. For example, a higher-order function that records execution time:import time def timer(func): def wrapper(): start = time.time() func() # Execute the original function end = time.time() print(f"Function execution time: {end - start:.2f} seconds") return wrapper # Return the new function def my_function(): time.sleep(1) # Manually wrapping the function my_function = timer(my_function) my_function() # Call the enhanced function -
Decorator Syntactic Sugar: The @ Symbol
Using@decorator_namesimplifies the wrapping process, equivalent to manual assignment:@timer # Equivalent to my_function = timer(my_function) def my_function(): time.sleep(1) my_function() # Directly call to enable enhanced functionality -
Handling Functions with Parameters
If the original function has parameters, the inner function of the decorator must define compatible parameters:def timer(func): def wrapper(*args, **kwargs): # Accept any arguments start = time.time() result = func(*args, **kwargs) # Pass arguments to the original function end = time.time() print(f"Execution time: {end - start:.2f} seconds") return result # Return the original function's result return wrapper @timer def greet(name, age): return f"{name} is {age} years old." print(greet("Bob", 20)) # Pass parameters normally -
Decorators with Their Own Parameters
If parameters need to be passed to the decorator (e.g.,@decorator(param=1)), two levels of nesting are required:def repeat(n=3): # Outer layer receives decorator parameters def decorator(func): # Middle layer receives the decorated function def wrapper(*args, **kwargs): for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(n=2) # Specify the number of repetitions def say_hello(): print("Hello!") say_hello() # Outputs "Hello!" twice -
Preserving the Original Function's Metadata
Decorators may overwrite the original function's name, documentation, and other metadata. Usefunctools.wrapsto fix this:from functools import wraps def timer(func): @wraps(func) # Copy the original function's metadata to wrapper def wrapper(*args, **kwargs): # ... same as above return wrapper @timer def example(): """Example function documentation""" pass print(example.__name__) # Outputs "example" instead of "wrapper"
Summary:
Decorators achieve functionality enhancement through function nesting and parameter passing. Their core lies in understanding functions as objects, closures, and scope. In practical applications, combining *args, **kwargs, and functools.wraps allows you to write universal and robust decorators.