当前位置:首页 > Python > 正文内容

Python装饰器:从基础到进阶的完整指南

admin6个月前 (03-21)Python182

装饰器(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中进阶编程的重要技能,希望这篇教程能帮助你深入理解它的工作原理和应用场景。在实际项目中,善用装饰器可以让你的代码更加简洁、优雅、可维护。

相关文章

Python装饰器完全指南:从基础到高级应用

装饰器是 Python 中最强大也最容易被误解的特性之一。很多初学者听说过装饰器,但总是感觉云里雾里,不敢在实际项目中使用。本文从最基础的概念讲起,逐步深入到高级应用场景,通过大量原创示例代码帮助...

Python 异步编程实战:从入门到精通

在 Python 开发中,我们经常会遇到需要同时处理多个 I/O 操作的场景。比如同时向多个 API 发送请求、批量下载文件、或者处理实时数据流。传统的同步方式会阻塞主线程,导致性能瓶颈。而异步编程通...

Python装饰器实战:从入门到精通的5个实用技巧

Python装饰器(Decorator)是Python中最强大的特性之一,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。从Web框架的日志记录到API的权限验证,装饰器无处不在。本文将通...

Python 列表推导式与生成器的高级用法

在日常 Python 开发中,我们经常需要对序列数据进行转换和过滤。传统的循环写法虽然直观,但往往代码冗长。Python 的列表推导式提供了一种优雅的替代方案,它不仅代码更简洁,而且执行效率通常更高。...

Python 多线程基础与 ThreadPoolExecutor 实战

在 Python 中,多线程是实现并发的一种常见方式,尤其在处理 I/O 密集型任务时效果明显。本文从线程的基本概念入手,演示如何使用 threading.Thread 模块创建线程,并对比使用 Th...

Python 类型注解完整指南与最佳实践

引言 随着 Python 项目的规模增长,代码的可维护性和类型安全性变得越来越重要。Python 3.5 引入了类型注解(Type Hints)机制,允许开发者在代码中标注变量和函数的类型信息。虽然...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。