Python装饰器从入门到精通
装饰器是 Python 中最强大也最优雅的特性之一。它允许我们在不修改原有函数代码的情况下,动态地为函数添加额外功能。本文将带你从基础概念逐步深入,掌握装饰器的核心原理和实战应用。
一、理解装饰器的本质
装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。这种设计模式使得我们能够在函数执行前后添加逻辑,而无需修改函数本身的代码。
最基础的装饰器实现如下:
def my_decorator(func):\n def wrapper():\n print("执行前...")\n func()\n print("执行后...")\n return wrapper\n\n@my_decorator\ndef say_hello():\n print("Hello!")当我们调用 say_hello() 时,实际上是调用了 wrapper() 函数,它会在原函数前后添加额外的打印语句。
二、带参数的装饰器
实际开发中,我们的函数往往需要接收参数。为了让装饰器具有通用性,我们需要使用 *args 和 **kwargs 来捕获任意参数:
def timer_decorator(func):\n def wrapper(*args, **kwargs):\n import time\n start_time = time.time()\n result = func(*args, **kwargs)\n end_time = time.time()\n print(f"函数执行时间: {end_time - start_time:.4f} 秒")\n return result\n return wrapper\n\n@timer_decorator\ndef calculate_sum(n):\n return sum(range(n))这样,无论函数接收何种参数,装饰器都能正确处理。关键是要在 wrapper 函数中返回原函数的执行结果,否则调用方将无法获得预期的返回值。
三、带参数的装饰器工厂
有时候我们需要为装饰器本身传递参数,这时就需要使用装饰器工厂。这是一个返回装饰器的函数:
def repeat(times):\n def decorator(func):\n def wrapper(*args, **kwargs):\n results = []\n for _ in range(times):\n results.append(func(*args, **kwargs))\n return results\n return wrapper\n return decorator\n\n@repeat(times=3)\ndef greet(name):\n return f"Hello, {name}!"调用 greet("World") 会返回一个包含三次调用结果的列表。注意这里有三层嵌套函数:外层接收装饰器参数,中层接收目标函数,内层是实际执行的包装函数。
四、使用 functools.wraps
当你使用装饰器包装函数时,原函数的元信息(如 __name__、__doc__)会被覆盖。为了保留这些信息,应该使用 functools.wraps:
from functools import wraps\n\ndef log_decorator(func):\n @wraps(func)\n def wrapper(*args, **kwargs):\n print(f"调用函数: {func.__name__}")\n return func(*args, **kwargs)\n return wrapper\n\n@log_decorator\ndef important_function():\n """这是一个重要的函数"""\n pass这样,important_function.__name__ 仍然是 "important_function",而不是 "wrapper"。
五、类装饰器
除了函数装饰器,Python 还支持类装饰器。类装饰器需要实现 __call__ 方法,使实例可以像函数一样被调用:
class CountCalls:\n def __init__(self, func):\n self.func = func\n self.count = 0\n\n def __call__(self, *args, **kwargs):\n self.count = 1\n print(f"函数已调用 {self.count} 次")\n return self.func(*args, **kwargs)\n\n@CountCalls\ndef process_data(data):\n return [x * 2 for x in data]类装饰器特别适合需要维护状态的场景,比如上面的调用计数器。
六、实战应用场景
1. 缓存装饰器:使用缓存避免重复计算
from functools import lru_cache\n\n@lru_cache(maxsize=128)\ndef fibonacci(n):\n if n < 2:\n return n\n return fibonacci(n-1) fibonacci(n-2)2. 权限验证装饰器:检查用户权限
def require_permission(permission):\n def decorator(func):\n @wraps(func)\n def wrapper(*args, **kwargs):\n user = get_current_user() # 假设的获取当前用户函数\n if not user.has_permission(permission):\n raise PermissionError("权限不足")\n return func(*args, **kwargs)\n return wrapper\n return decorator3. 异常处理装饰器:统一处理异常
def handle_exception(exception_type):\n def decorator(func):\n @wraps(func)\n def wrapper(*args, **kwargs):\n try:\n return func(*args, **kwargs)\n except exception_type as e:\n print(f"发生错误: {e}")\n return None\n return wrapper\n return decorator七、装饰器叠加顺序
当多个装饰器叠加作用于同一个函数时,执行顺序是从内到外:
@decorator1\n@decorator2\n@decorator3\ndef my_function():\n pass实际执行顺序是:decorator1(decorator2(decorator3(my_function)))。理解这个顺序对于调试装饰器问题非常重要。
八、最佳实践
1. 始终使用 functools.wraps 保留函数元信息
2. 装饰器应该尽可能轻量,避免复杂逻辑
3. 使用描述性的装饰器名称
4. 考虑使用类装饰器当需要维护状态时
5. 记得在 wrapper 中返回原函数的结果
装饰器是 Python 编程中不可或缺的工具。掌握装饰器不仅能让代码更加优雅,还能显著提高代码的可维护性和复用性。通过合理使用装饰器,我们可以轻松实现日志记录、性能监控、缓存、权限验证等横切关注点,让业务逻辑保持清晰简洁。
