🐍 Python 装饰器基础笔记
1. 核心概念与思维导图
装饰器本质上是一个高阶函数,也是闭包的一种特例应用。它允许你在不修改原函数代码、不改变原函数调用方式的前提下,为函数动态添加额外功能(如日志、计时、权限校验等)。
🧠 知识点思维导图
2. 基础装饰器:无参模式
这是最简单的装饰器形式,适用于不需要额外配置的场景。
2.1 核心结构
- 外层函数:接收被装饰的函数
func。 - 内层函数 (Wrapper):定义增强逻辑,调用
func。 - 返回:返回内层函数对象。
2.2 代码示例:登录与退出功能
def decorator(func):
def wrapper():
print(" 登陆功能 (前置增强)")
func() # 执行原函数
print(" 退出登录 (后置增强)")
return wrapper
# 使用语法糖 @decorator 等价于 comment = decorator(comment)
@decorator
def comment():
print("评论功能")
@decorator
def download():
print("下载功能")
# 调用
comment()
# 输出:
# 登陆功能
# 评论功能
# 退出登录
3. 进阶一:处理被装饰函数的参数
如果原函数需要参数,装饰器的内层函数 wrapper 必须能够接收这些参数,并使用 *args 和 **kwargs 透传给原函数。
3.1 代码示例:通用日志记录
import time
def logging(func):
def wrapper(*args, **kwargs):
# 1. 前置处理:打印函数名
print(f"日志信息: {func.__name__} 开始执行")
# 2. 执行原函数:必须传递参数
func(*args, **kwargs)
# 3. 后置处理
print(f"日志信息: {func.__name__} 结束执行")
return wrapper
@logging
def sum_num(*args, **kwargs):
result = 0
result += sum(args)
result += sum(kwargs.values())
print(f"计算结果是:{result}")
# 调用带参函数
sum_num(1, 2, 3, a=1, b=2)
4. 进阶二:处理返回值
很多时候我们需要获取原函数的执行结果。装饰器的 wrapper 必须接收原函数的返回值,并将其返回,否则调用被装饰的函数将得到 None。
4.1 代码示例:保留返回值
def logging(func):
def wrapper(*args, **kwargs):
print(f"日志信息: {func.__name__} 开始执行")
# 关键点:接收返回值
result = func(*args, **kwargs)
print("这里是函数执行中...")
print(f"日志信息: {func.__name__} 结束执行")
# 关键点:返回结果
return result
return wrapper
@logging
def sum_num(*args, **kwargs):
result = sum(args) + sum(kwargs.values())
return result
# 验证返回值
res = sum_num(1, 2, a=1)
print(f"最终获取到的返回值: {res}")
5. 进阶三:装饰器本身带参数
如果装饰器需要根据不同的配置(如日志级别、重试次数)来改变增强逻辑,就需要三层嵌套。
5.1 结构解析
- 最外层:接收装饰器参数 (
flag),返回装饰器。 - 中间层:接收被装饰函数 (
func),返回 wrapper。 - 最内层:接收函数参数 (
*args),执行逻辑。
5.2 代码示例:可配置的日志级别
def logging(flag): # 1. 接收装饰器参数
def decorator(func): # 2. 接收被装饰函数
def wrapper(*args, **kwargs): # 3. 接收函数调用参数
if flag == "info":
print(f"ℹ️ 日志信息: {func.__name__} 开始执行")
elif flag == "warn":
print(f"⚠️ 警告信息: {func.__name__} 开始执行")
result = func(*args, **kwargs)
if flag == "info":
print(f"ℹ️ 日志信息: {func.__name__} 结束执行")
elif flag == "warn":
print(f"⚠️ 警告信息: {func.__name__} 结束执行")
return result
return wrapper
return decorator
# 使用不同的参数装饰同一个逻辑(或不同函数)
@logging("info")
def sum_num_info(*args, **kwargs):
return sum(args) + sum(kwargs.values())
@logging("warn")
def sum_num_warn(*args, **kwargs):
return sum(args) + sum(kwargs.values())
sum_num_info(1, 2)
sum_num_warn(1, 2)
6. 实战案例:性能计时器
统计代码执行时间是装饰器最经典的用途之一。
import time
def timer(func):
def wrapper():
start = time.time() # 记录开始时间
func() # 执行函数
end = time.time() # 记录结束时间
print(f"⏱️ 这个函数的执行时间: {end - start:.6f} 秒")
return wrapper
@timer
def timer_demo():
# 模拟耗时操作
total = 0
for i in range(10000000):
total += i
timer_demo()
7. 最佳实践:@wraps
在使用装饰器后,原函数的元信息(如 __name__, __doc__)会被 wrapper 覆盖。为了保持元数据,强烈建议使用 functools.wraps。
from functools import wraps
def my_decorator(func):
@wraps(func) # 修复元数据丢失问题
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
总结
- 闭包是基础:嵌套 + 引用 + 返回。python
- 万能参数:内层函数永远建议使用
*args, **kwargs以适配所有函数。 - 返回值:不要忘记
return result。 - 带参装饰器:记住“三层嵌套”结构。