Python @装饰器用法详解
当然!让我用更详细的方式重新解释自定义装饰器。
装饰器的基本概念
装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。它"装饰"或"包装"原始函数,在不修改原函数代码的情况下添加新功能。
简单比喻
想象装饰器就像给你的函数"穿上一件外套":
- 原始函数是"身体"
- 装饰器是"外套"
- 穿上外套后,函数有了新功能,但核心功能不变
逐步理解
第一步:没有装饰器的版本
def greet():print("Hello!")# 如果我们想在每次调用greet时记录时间
import timedef greet_with_timing():start = time.time()greet() # 调用原始函数end = time.time()print(f"函数执行了 {end-start:.2f} 秒")greet_with_timing()
这样写的问题:每次都要手动包装,代码重复。
第二步:手动创建装饰器
def timer_decorator(func):def wrapper():start = time.time()func() # 调用原始函数end = time.time()print(f"{func.__name__} 执行了 {end-start:.2f} 秒")return wrapper# 手动应用装饰器
greet = timer_decorator(greet)
greet() # 现在会自动计时
第三步:使用@语法糖
@timer_decorator # 这行等同于:greet = timer_decorator(greet)
def greet():print("Hello!")@timer_decorator
def say_goodbye():print("Goodbye!")greet() # 自动计时
say_goodbye() # 自动计时
更实用的例子
1. 日志记录装饰器
def logger(func):def wrapper(*args, **kwargs):print(f"开始执行函数: {func.__name__}")print(f"参数: {args} {kwargs}")result = func(*args, **kwargs)print(f"函数 {func.__name__} 执行完成,返回值: {result}")return resultreturn wrapper@logger
def add(a, b):return a + b@logger
def multiply(x, y):return x * yadd(3, 5)
# 输出:
# 开始执行函数: add
# 参数: (3, 5) {}
# 函数 add 执行完成,返回值: 8multiply(4, 6)
# 输出:
# 开始执行函数: multiply
# 参数: (4, 6) {}
# 函数 multiply 执行完成,返回值: 24
2. 权限验证装饰器
def login_required(func):def wrapper(user, *args, **kwargs):if user.get('is_authenticated', False):return func(user, *args, **kwargs)else:return "请先登录!"return wrapper@login_required
def view_profile(user):return f"欢迎 {user['username']} 查看个人资料"# 测试
user1 = {'username': 'Alice', 'is_authenticated': True}
user2 = {'username': 'Bob', 'is_authenticated': False}print(view_profile(user1)) # 欢迎 Alice 查看个人资料
print(view_profile(user2)) # 请先登录!
3. 重试装饰器
def retry(max_attempts=3):def decorator(func):def wrapper(*args, **kwargs):for attempt in range(max_attempts):try:return func(*args, **kwargs)except Exception as e:print(f"第{attempt+1}次尝试失败: {e}")if attempt == max_attempts - 1:raisereturn Nonereturn wrapperreturn decorator@retry(max_attempts=3)
def unstable_connection():import randomif random.random() < 0.7: # 70%概率失败raise ConnectionError("连接失败")return "连接成功!"# 多次运行这个函数,观察重试机制
result = unstable_connection()
print(result)
装饰器的工作原理
# 理解这个等价关系很重要:
@decorator
def my_function():pass# 完全等同于:
def my_function():pass
my_function = decorator(my_function)
为什么要用装饰器?
- 代码复用:一次编写,多处使用
- 关注点分离:核心逻辑和附加功能分开
- 不修改原代码:保持原始函数的纯净
- 灵活组合:可以叠加多个装饰器
@logger
@timer_decorator
@login_required
def complex_operation(user, data):# 这个函数同时拥有了日志、计时、权限验证功能return "操作完成"
总结
自定义装饰器就是创建一个包装函数,它:
- 接受一个函数作为输入
- 添加一些额外功能
- 返回一个新的函数(通常调用原始函数)
- 使用
@装饰器名
语法来应用
这样你就能在不修改原函数的情况下,给函数添加各种通用功能!
完全正确!你的理解非常准确!
@timer_decorator
def greet():print("Hello!")
确实完全等同于:
def greet():print("Hello!")
greet = timer_decorator(greet)
验证这个等价关系
让我们用代码来验证:
def timer_decorator(func):def wrapper():print("开始计时...")result = func()print("计时结束")return resultreturn wrapper# 方法1:使用@语法
@timer_decorator
def greet1():print("Hello!")# 方法2:手动赋值
def greet2():print("Hello!")
greet2 = timer_decorator(greet2)# 测试两个函数
print("=== greet1 ===")
greet1()print("\n=== greet2 ===")
greet2()print(f"\n两个函数相同吗? {greet1 == greet2}") # 应该是 False,因为它们是不同的包装函数实例
print(f"但功能相同吗?让我们看看输出...")
输出结果:
=== greet1 ===
开始计时...
Hello!
计时结束=== greet2 ===
开始计时...
Hello!
计时结束两个函数相同吗? False
但功能相同吗?让我们看看输出...
深入理解装饰器的执行时机
def my_decorator(func):print(f"装饰器被执行了!要装饰的函数是: {func.__name__}")def wrapper():print("包装函数被调用")return func()return wrapperprint("=== 定义阶段 ===")
@my_decorator
def my_function():print("原始函数被调用")print("\n=== 调用阶段 ===")
my_function()
输出:
=== 定义阶段 ===
装饰器被执行了!要装饰的函数是: my_function=== 调用阶段 ===
包装函数被调用
原始函数被调用
关键点:
- 装饰器在函数定义时立即执行,而不是在函数调用时
@decorator
这行代码在模块加载时就会运行- 它实际上调用了
decorator
函数,并把被装饰的函数作为参数传递
为什么要有@语法糖?
如果没有@语法,代码会变得很冗长:
# 没有@语法
def func1(): pass
func1 = decorator1(func1)def func2(): pass
func2 = decorator2(func2)def func3(): pass
func3 = decorator3(func3)# 使用@语法
@decorator1
def func1(): pass@decorator2
def func2(): pass@decorator3
def func3(): pass
@语法让代码:
- 更清晰:装饰器紧挨着函数定义
- 更易读:一眼就能看出函数被装饰了
- 更符合直觉:装饰在"上面",就像给函数"戴帽子"
所以你的理解完全正确!@decorator
就是 Python 提供的一种语法糖,让装饰器的使用更加简洁美观。