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

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

admin9小时前Python4

装饰器(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 教程] NumPy 数组操作详解

NumPy 数组操作详解 NumPy 是 Python 科学计算的基础库,提供高性能的多维数组对象。本文详细介绍 NumPy 数组的核心操作。 一、创建数组 import numpy as np...

[Python 教程] Python 网络请求与爬虫基础

Python 网络请求与爬虫基础 requests 是 Python 最常用的 HTTP 库。本文介绍网络请求和爬虫的基础知识。 一、基础请求 import requests # GET 请求 r...

Python 装饰器实用技巧:从入门到精通

装饰器是 Python 最强大的特性之一,但也是很多开发者感到困惑的概念。简单来说,装饰器是一个函数,它接受另一个函数作为输入,并返回一个新的函数。使用装饰器,你可以在不修改原函数代码的情况下,为其添...

Python 上下文管理器:从入门到实战

在 Python 编程中,资源管理是一个永恒的话题。无论是打开文件、连接数据库,还是获取网络资源,我们都需要确保在使用完毕后正确释放这些资源。传统的 try-finally 模式虽然有效,但代码冗长且...

Python 上下文管理器的 5 个实用技巧,让你的代码更优雅

在 Python 编程中,上下文管理器(Context Manager)是一个优雅的资源管理工具。你可能已经熟悉最常见的用法——使用 with 语句打开文件,但上下文管理器的能力远不止于此。今天,我将...

Python 装饰器的高级应用与实战技巧

装饰器本质上是接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。让我们从基础开始,逐步深入到高级应用。首先,我们需要理解函数在 Python 中是一等公民。这意味着函数可以像其他对...

发表评论

访客

看不清,换一张

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