Python
基础语法
01概念
02安装
03变量
04字符串
05数
06常量与注释
07列表
08元组
09if语句
10字典
11集合
12复合数据类型对比
13推导式
14用户输入
15while循环
16函数
17类
18面向对象编程
19文件操作
20异常处理
21日期和时间
22魔术方法
23内置函数
24线程
25并发&并行
26正则表达式
27迭代器
28装饰器
29生成器
30上下文管理器
31函数式编程
32闭包
33解包
34工具库
35连接关系型数据库
36虚拟环境
37异步编程
网络爬虫
01urllib库[了解]
02requests库
03数据交换格式
04解析库
05lxml
06Beautiful Soup
07Xpath语法
08动态网页的处理
-
+
首页
28装饰器
装饰器(Decorator)是 Python 中一种基于**函数式编程思想**的高级特性,用于在**不修改原有函数 / 类源代码**的前提下,动态地为其添加额外功能(如日志记录、性能统计、权限校验等)。它本质上是一个 “包装函数”,通过包裹目标对象并返回新的对象,实现对目标功能的增强或扩展,是 “开放 - 封闭原则”(对扩展开放,对修改封闭)的典型实践。 ## 定义 装饰器是**接收一个函数 或 类作为参数,并返回一个新函数 / 类的可调用对象**(通常是函数)。其核心作用是:在不改变目标对象(函数 或 类)代码和调用方式的前提下,为其附加额外逻辑。 - **通俗理解**:装饰器就像 “包装纸”,目标函数是 “礼物”。包装纸不改变礼物本身,但能给礼物增加装饰(如花纹、标签);同理,装饰器不修改原函数代码,但能给函数增加功能(如日志、计时)。 - **本质**:装饰器是 “高阶函数” 的延伸(接收函数并返回函数),结合了函数嵌套和闭包的特性,实现对目标函数的 “动态增强”。 ## 基本语法 Python 通过 **@符号 ** 提供装饰器的 “语法”,简化装饰器的使用。 语法结构: ```python # 定义装饰器(接收函数,返回新函数) def decorator(func): def wrapper(*args, **kwargs): # 装饰器附加逻辑(调用原函数前) result = func(*args, **kwargs) # 调用原函数 # 装饰器附加逻辑(调用原函数后) return result # 返回原函数结果 return wrapper # 返回包装函数 # 使用装饰器(@语法) @decorator def target_function(parameters): # 目标函数核心逻辑 pass ``` - @decorator 等价于 target_function = decorator(target_function):将原函数传入装饰器,再将装饰器返回的新函数(wrapper)重新赋值给原函数名。 - wrapper(*args, **kwargs):包装函数需支持任意参数(*args接收位置参数,**kwargs接收关键字参数),确保能适配被装饰函数的各种参数形式。 例如:为函数添加 “调用时打印日志” 的功能。 ```python def log_decorator(func): def wrapper(*args, **kwargs): # 调用前:打印函数名和参数 print(f"调用函数:{func.__name__},参数:{args}, {kwargs}") # 调用原函数 result = func(*args, **kwargs) # 调用后:打印返回值 print(f"函数{func.__name__}返回:{result}") return result return wrapper # 用装饰器增强函数 @log_decorator def add(a, b): return a + b # 调用被装饰的函数(调用方式不变) add(2, 3) ``` 输出结果: ```python 调用函数:add,参数:(2, 3), {} 函数add返回:5 ``` - 原函数add的代码未修改,但其功能被扩展了日志打印。 - 调用方式仍为add(2, 3),与装饰前一致。 ## 执行流程 装饰器的执行可分为 **“定义阶段”**和**“调用阶段”**: **定义阶段**: 当解释器执行到@decorator时,会立即调用decorator(target_function),将原函数传入装饰器,并将返回的wrapper函数赋值给target_function。此时原函数已被 “替换” 为wrapper,但原函数的逻辑被保存在wrapper内部(通过闭包引用)。 **调用阶段**: 当调用target_function(...)时,实际调用的是wrapper(...): - 执行wrapper中的 “前置逻辑”(如日志打印); - 调用原函数func(*args, **kwargs),获取结果; - 执行wrapper中的 “后置逻辑”(如返回值打印); - 返回结果(保持与原函数一致的返回行为)。 ## 带参数的装饰器 当装饰器需要**动态调整功能**(如日志级别、超时时间)时,需为装饰器本身添加参数。此时需在基础装饰器外再包裹一层 “参数接收函数”,形成**三层函数结构**。 语法结构: ```python # 第一层:接收装饰器参数,返回真正的装饰器 def decorator_with_params(param1, param2): # 第二层:真正的装饰器(接收函数,返回包装函数) def decorator(func): # 第三层:包装函数(附加逻辑+调用原函数) def wrapper(*args, **kwargs): # 使用装饰器参数param1, param2 result = func(*args, **kwargs) return result return wrapper return decorator # 返回真正的装饰器 # 使用带参数的装饰器 @decorator_with_params(p1, p2) def target_function(): pass ``` 执行逻辑:@decorator_with_params(p1, p2) 等价于 target_function = decorator_with_params(p1, p2)(target_function)。 例如:带日志级别的装饰器。 ```python def log_decorator(level="INFO"): def decorator(func): def wrapper(*args, **kwargs): # 使用装饰器参数level print(f"[{level}] 调用函数:{func.__name__}") result = func(*args, **kwargs) print(f"[{level}] 函数{func.__name__}执行完毕") return result return wrapper return decorator # 装饰函数时指定日志级别 @log_decorator(level="DEBUG") def multiply(a, b): return a * b multiply(3, 4) ``` 输出结构: ```python [DEBUG] 调用函数:multiply [DEBUG] 函数multiply执行完毕 ``` ## 对原函数元信息 装饰器会默认 “覆盖” 原函数的元信息(如\_\_name\_\_、\_\_doc\_\_、\_\_module\_\_),因为被装饰后的函数实际是wrapper,而非原函数。这可能导致调试困难(如help()显示错误信息)。 问题示例: ```python def decorator(func): def wrapper(): """这是包装函数""" func() return wrapper @decorator def original(): """这是原函数""" pass print(original.__name__) # 输出:wrapper(应为original) print(original.__doc__) # 输出:这是包装函数(应为这是原函数) ``` 修复方案:functools.wraps是 Python 提供的工具,用于将原函数的元信息 “复制” 到包装函数,保持元信息一致性。 ```python import functools def decorator(func): # 使用@functools.wraps(func)修复元信息 @functools.wraps(func) def wrapper(): """这是包装函数""" func() return wrapper @decorator def original(): """这是原函数""" pass print(original.__name__) # 输出:original(正确) print(original.__doc__) # 输出:这是原函数(正确) ``` 最佳实践:**所有装饰器的包装函数都应使用@functools.wraps(func)**,确保元信息正确。 ## 类装饰器 装饰器不仅可以是函数,也可以是**类**。类装饰器有两种形式: 1. 装饰类的装饰器(增强类的功能); 2. 作为装饰器的类(类本身是装饰器,通过__call__方法实现)。 **1. 装饰类的装饰器(函数装饰器)** 为类动态添加属性或方法,示例:为类添加 “创建时间” 属性: ```python import time import functools def add_create_time(cls): # 为类添加类属性:创建时间 cls.create_time = time.time() return cls @add_create_time class MyClass: pass print(MyClass.create_time) # 输出类被创建时的时间戳(如1695000000.123) ``` **2. 作为装饰器的类(类装饰器)** 类通过实现\_\_init\_\_(接收被装饰函数)和\_\_call\_\_(实现包装逻辑)方法,成为装饰器。适合需要**保存状态**的场景(如计数、缓存)。 示例:统计函数调用次数的类装饰器: ```python import functools class CountCalls: def __init__(self, func): # 初始化:保存被装饰函数,初始化计数器 self.func = func self.count = 0 # 修复元信息 functools.update_wrapper(self, func) def __call__(self, *args, **kwargs): # 实现装饰逻辑:调用次数+1,再执行原函数 self.count += 1 print(f"函数{self.func.__name__}已调用{self.count}次") return self.func(*args, **kwargs) @CountCalls # 等价于:func = CountCalls(func) def greet(name): return f"Hello, {name}!" greet("Alice") # 输出:函数greet已调用1次 → 返回"Hello, Alice!" greet("Bob") # 输出:函数greet已调用2次 → 返回"Hello, Bob!" print(greet.count) # 输出:2(访问计数器) ``` 类装饰器的优势:通过实例变量(如self.count)轻松保存状态,比函数装饰器(需用闭包变量)更直观。 ## 适用场景 **1. 日志记录** 自动记录函数的调用时间、参数、返回值等,用于调试或审计。 **2. 性能计时** 统计函数执行时间,分析性能瓶颈。 **3. 权限校验** 在函数执行前验证用户权限,无权限则拒绝执行。 **4. 缓存(记忆化)** 缓存函数的计算结果,避免重复计算(适用于耗时且参数固定的函数)。 **5. 输入验证** 检查函数参数是否符合要求,提前拦截无效输入。
毛林
2025年9月7日 11:45
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码