我已经在不同的代码库中看到过,只读了PyMOTW(参见第一个注释here)。
解释说,如果将回溯分配给来自sys.exc_info()[2]
的变量,将创建一个循环,但为什么会这样?
这有多大的问题?我应该在代码库中搜索exc_info
的所有用法并确保删除回溯吗?
答案 0 :(得分:20)
Python 3(更新为原始答案):
在Python 3中,问题中引用的建议已从Python文档中删除。我的原始答案(以下)仅适用于在其文档中包含引用的Python版本。
Python 2:
Python垃圾收集器最终会找到并删除循环引用,就像通过从其中一个堆栈框架内部引用回溯堆栈而创建的循环引用一样,所以不要返回并重写代码。但是,展望未来,你可以遵循
的建议http://docs.python.org/library/sys.html
(文件exc_info()
)并说:
exctype, value = sys.exc_info()[:2]
当你需要抓住例外时。
还有两个想法:
首先,你为什么要运行exc_info()
?
如果你想捕捉异常,你不应该只说:
try:
...
except Exception as e: # or "Exception, e" in old Pythons
... do with with e ...
而不是使用sys
模块中的对象进行混淆?
第二:好的,我已经给出了很多建议,但没有真正回答你的问题。 : - )
为什么要创建一个循环?嗯,在简单的情况下,当对象引用自身时会创建一个循环:
a = [1,2,3]
a.append(a)
当两个物体相互引用时:
a = [1,2,3]
b = [4,5,a]
a.append(b)
在这两种情况下,当函数结束时,变量值仍然存在,因为它们被锁定在引用计数容器中:在另一个先消失之前,它们都不会消失!只有现代的Python垃圾收集器才能通过最终注意到循环并打破它来解决这个问题。
因此理解这种情况的关键是“追溯”对象 - exc_info()
返回的第三件事(在索引#2处) - 包含每个函数的“堆栈帧”被称为例外。那些堆栈帧是不“死”对象,显示当调用execption时 为真;帧仍然活着!捕获异常的函数仍然存在,因此它的堆栈框架是一个生物,仍然在增长和丢失变量引用,因为它的代码执行来处理异常(并且当它完成“except”子句时执行其他任何操作关于它的工作)。
所以当你说t = sys.exc_info()[2]
时,回溯中的其中一个堆栈框架 - 实际上属于当前正在运行的函数的框架 - 现在有一个名为t
的变量它指向堆栈框架本身,创建一个循环,就像我上面展示的那样。
答案 1 :(得分:11)
回溯包含对所有活动帧的引用,这些活动帧又包含对那些不同帧中所有局部变量的引用 - 这些引用是回溯和帧对象的重要部分,因此这并不令人惊讶。因此,如果你将一个引用添加回traceback(或者在临时添加它时未能及时删除它),你就不可避免地形成一个大的引用循环 - 这会干扰垃圾收集(如果任何一个对象可能会完全阻止它)在循环中属于覆盖__del__
的终结类方法的类。
特别是在一个长期运行的程序中,干扰垃圾收集并不是最好的想法,因为你将保留你并不真正需要的内存(超过必要的时间,或者如果你基本上是无限期的话)通过让它们包含带有终结器的对象来阻止这些循环上的垃圾收集。
因此,无论是否来自exc_info
,最好尽快摆脱追溯!