Python装饰器双重包装问题:嵌套事务引发属性删除异常求助
解决Python装饰器嵌套调用导致的事务重复执行与属性删除异常问题
这是个非常典型的装饰器嵌套调用坑!我来给你拆解问题并给出解决方案:
问题根源
你的transaction装饰器现在是每次调用被装饰函数都会完整执行一遍事务初始化(设置timestamp)和清理(删除timestamp)逻辑。当fn1调用fn2时,fn2的装饰器也会触发,导致:
- 事务被嵌套执行两次
timestamp被重复设置,然后在fn2的finally块里先被删掉,等fn1的finally块再执行delattr时,属性已经不存在,直接抛出异常。
解决方案:让装饰器识别嵌套调用,跳过重复的事务逻辑
我们只需要在装饰器里加一个判断:如果当前已经处于事务中(即env已经有timestamp属性),就直接执行原函数,不重复执行事务的初始化和清理步骤。
修改后的装饰器代码示例:
def transaction(func): def wrapper(env, *args, **kwargs): # 检查是否已经在事务上下文里 if hasattr(env, 'timestamp'): # 直接执行原函数,跳过事务逻辑 return func(env, *args, **kwargs) # 首次进入事务,执行初始化 setattr(env, 'timestamp', your_timestamp_value) try: # 执行原函数,包括可能的嵌套调用 return func(env, *args, **kwargs) finally: # 事务结束,清理属性(额外加判断避免意外) if hasattr(env, 'timestamp'): delattr(env, 'timestamp') return wrapper
为什么这样能解决问题?
当fn1被调用时,装饰器检查到env没有timestamp,就设置属性并执行fn1;fn1内部调用fn2时,fn2的装饰器检查到env已经有timestamp,直接执行fn2的逻辑,不会重复设置/删除属性。等fn2执行完回到fn1,fn1的finally块才会删除timestamp,完美避免了重复操作和异常。
进阶优化(多线程场景)
如果你的代码是多线程环境,用env对象的属性判断可能不够安全(不同线程的env可能共享状态),可以改用threading.local来跟踪当前线程的事务状态:
import threading # 线程本地存储,跟踪每个线程的事务状态 _thread_local = threading.local() def transaction(func): def wrapper(env, *args, **kwargs): # 检查当前线程是否已在事务中 if getattr(_thread_local, 'in_transaction', False): return func(env, *args, **kwargs) _thread_local.in_transaction = True setattr(env, 'timestamp', your_timestamp_value) try: return func(env, *args, **kwargs) finally: delattr(env, 'timestamp') _thread_local.in_transaction = False return wrapper
这样即使多线程并发执行,每个线程的事务状态都是独立的,不会互相干扰。
内容的提问来源于stack exchange,提问作者user3758232
相关产品推荐
相关产品推荐

