我知道sys.exc_info文档说在处理回溯对象时要小心,但仍然不确定某些情况的安全性或不安全性。此外,文档说“警告:不要这样做!”,紧接着是“注意:实际上,没关系”,这进一步让我感到困惑。
在任何情况下,文档和“为什么需要在 Python 中显式删除 sys.exc_info() 回溯? ”(Alex Martelli 的回答)似乎暗示其唯一的局部变量引用分配给它们的回溯值导致一个问题。
这给我留下了几个问题:
- 在这种情况下,“局部变量”究竟是什么意思?我正在为术语而苦苦挣扎,但是:这是否意味着仅在函数中创建的变量,还是由函数参数创建的变量?范围内的其他变量呢,例如全局变量或自我?
- 闭包如何影响回溯的潜在循环引用?一般的想法是:闭包可以引用其封闭函数可以引用的所有内容,因此引用闭包的回溯最终可能会引用相当多的内容。我正在努力想出一个更具体的例子,但是一些组合:一个内部函数,返回 sys.exc_info() 的代码,在某个范围内的昂贵的短期对象。
随意告诉我我的结论或假设在哪里是错误的,因为在我写这篇文章时,我已经多次说服自己相信和不相信自己的陈述:)。
虽然我想要我的具体示例的答案,但我也在寻求关于如何在更深奥的情况下安全处理回溯的一般建议、知识或战争故事(例如,您必须运行一个循环并希望积累任何引发的异常,您必须生成一个新线程并需要报告其引发的任何异常,您必须创建闭包和回调,并且必须与引发的异常进行通信等)。
示例 1:执行错误处理的内部函数
def DoWebRequest():
thread, error_queue = CreateThread(ErrorRaisingFunc)
thread.start()
thread.join()
if not error_queue.empty():
# Purposefully not calling error_queue.get() for illustrative purposes
print 'error!'
def CreateThread(func):
error_queue = Queue.Queue()
def Handled():
try:
func()
except Exception:
error_queue.put(sys.exc_info())
thread = threading.Thread(target=Handled)
return thread, error_queue
闭包是否Handled()
会导致任何引发的异常引用error_queue
并导致循环引用,因为error_queue
还包含回溯?从error_queue
(即调用.get()
)中删除回溯是否足以消除循环引用?
示例 2:exc_info 范围内的长寿对象,或返回 exc_info
long_lived_cache = {}
def Alpha(key):
expensive_object = long_lived_cache.get(key)
if not expensive_object:
expensive_object = ComputeExpensiveObject()
long_lived_cache[key] = expensive_object
exc_info = AlphaSub(expensive_object)
if exc_info:
print 'error!', exc_info
def AlphaSub(expensive_object):
try:
ErrorRaisingFunc(expensive_object)
return None
except Exception:
return sys.exc_info()
引发的异常是否AlphaSub()
具有对 的引用expensive_object
,并且因为expensive_object
被缓存,所以回溯永远不会消失?如果是这样,如何打破这样的循环?
或者,exc_info
包含Alpha
堆栈帧,Alpha
堆栈帧包含对 的引用exc_info
,从而产生循环引用。如果是这样,如何打破这样的循环?