Python装饰器:从基础到进阶的完整指南
装饰器(Decorator)是Python编程语言中一个非常强大的工具。简单来说,装饰器是一个可以接受函数作为参数,并返回一个新函数的函数。这种设计模式让我们能够在不修改原有函数代码的情况下,为函数添加额外的功能,比如日志记录、性能计时、权限验证等。
理解装饰器的关键在于理解Python中的函数也是对象。这意味着函数可以被赋值给变量、作为参数传递、甚至作为返回值返回。这种"一等公民"的特性是装饰器能够工作的基础。
让我们从一个最简单的例子开始。假设我们有一个普通函数,我们想在它执行前后添加一些打印信息:
```python def simple_decorator(func): def wrapper(): print("执行前:准备调用函数") func() print("执行后:函数调用完成") return wrapper @simple_decorator def greet(): print("Hello, World!") greet() ```运行这段代码,你会看到:
``` 执行前:准备调用函数 Hello, World! 执行后:函数调用完成 ```这个例子展示了装饰器的基本工作原理。`@simple_decorator`语法实际上是`greet = simple_decorator(greet)`的语法糖。装饰器`simple_decorator`接收`greet`函数,返回一个新的`wrapper`函数,当我们调用`greet()`时,实际调用的是`wrapper()`,而`wrapper()`内部会调用原始的`greet`函数。
然而,这个简单的装饰器有个问题:它只能用于没有参数的函数。为了让装饰器更加通用,我们需要使用`*args`和`**kwargs`来接收任意参数:
```python def universal_decorator(func): def wrapper(*args, **kwargs): print(f"调用函数: {func.__name__}") print(f"参数: args={args}, kwargs={kwargs}") result = func(*args, **kwargs) print(f"返回值: {result}") return result return wrapper @universal_decorator def calculate(a, b, operation='add'): if operation == 'add': return a + b elif operation == 'multiply': return a * b calculate(5, 3, operation='multiply') ```现在我们的装饰器可以处理任何参数组合了。这是一个很重要的改进,因为它让装饰器更加实用和灵活。
但是,还有一个问题:使用装饰器后,函数的元信息(如`__name__`、`__doc__`等)会被wrapper函数覆盖。为了解决这个问题,Python提供了`functools.wraps`装饰器:
```python from functools import wraps def smart_decorator(func): @wraps(func) def wrapper(*args, **kwargs): """Wrapper函数的文档""" print(f"执行 {func.__name__}") return func(*args, **kwargs) return wrapper @smart_decorator def important_function(): """这是一个重要的函数""" pass print(important_function.__name__) # 输出: important_function print(important_function.__doc__) # 输出: 这是一个重要的函数 ```使用`@wraps(func)`后,`wrapper`函数会保留原始函数的元信息,这对于调试和文档生成非常重要。
装饰器在实际开发中有很多实用的场景。让我们看几个常见用例:
1. 性能计时装饰器:
```python 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__} 执行时间: {end_time - start_time:.4f}秒") return result return wrapper @timer def slow_function(): time.sleep(1) return "完成" slow_function() ```2. 缓存装饰器(简单版):
```python def cache(func): cached_results = {} @wraps(func) def wrapper(*args, **kwargs): key = (args, frozenset(kwargs.items())) if key not in cached_results: cached_results[key] = func(*args, **kwargs) return cached_results[key] return wrapper @cache def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) print(fibonacci(35)) # 第二次调用会非常快 ```3. 重试装饰器:
```python def retry(max_attempts=3, delay=1): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): import time for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: if attempt == max_attempts - 1: raise print(f"第 {attempt + 1} 次尝试失败,{delay}秒后重试...") time.sleep(delay) return wrapper return decorator @retry(max_attempts=3, delay=2) def unstable_operation(): import random if random.random() < 0.7: raise ValueError("随机失败") return "成功" unstable_operation() ```这个重试装饰器展示了带参数的装饰器的写法。注意这里的装饰器工厂模式:`retry`返回一个装饰器,而装饰器再返回wrapper函数。
类也可以作为装饰器。类装饰器通过实现`__call__`方法来工作:
```python class CountCalls: def __init__(self, func): self.func = func self.count = 0 def __call__(self, *args, **kwargs): self.count += 1 print(f"{self.func.__name__} 已被调用 {self.count} 次") return self.func(*args, **kwargs) @CountCalls def popular_function(): return "结果" popular_function() popular_function() popular_function() ```类装饰器的优点是可以维护状态(如调用次数),这在某些场景下非常有用。
Python还支持多个装饰器叠加使用。当多个装饰器应用到一个函数上时,它们从下往上执行,就像洋葱剥层一样:
```python def log(func): @wraps(func) def wrapper(*args, **kwargs): print(f"--> 进入 {func.__name__}") result = func(*args, **kwargs) print(f"<-- 离开 {func.__name__}") return result return wrapper def uppercase(func): @wraps(func) def wrapper(*args, **kwargs): result = func(*args, **kwargs) return result.upper() if isinstance(result, str) else result return wrapper @log @uppercase def process_text(text): return text process_text("hello world") ```输出结果:
``` --> 进入 process_text <-- 离开 process_text HELLO WORLD ```注意执行顺序:`@uppercase`先应用(内层),然后`@log`(外层)。但是实际执行时,`@log`的wrapper先运行(进入打印),然后调用内部函数,`@uppercase`的wrapper运行(转换大写),最后`@log`的wrapper打印离开信息。
掌握装饰器后,你的代码会变得更加优雅和Pythonic。装饰器遵循了开放封闭原则(对扩展开放,对修改封闭),让代码更易于维护和扩展。记住这些关键点:使用`functools.wraps`保留元信息、使用`*args`和`**kwargs`处理任意参数、理解装饰器的执行顺序、合理使用带参数的装饰器工厂。
装饰器是Python中进阶编程的重要技能,希望这篇教程能帮助你深入理解它的工作原理和应用场景。在实际项目中,善用装饰器可以让你的代码更加简洁、优雅、可维护。
