背景:我正在用Python编写National Instruments的TestStand的COM编程。 TestStand会抱怨对象是否未正确“释放”(弹出“未正确释放的对象”调试对话框)。在Python中发布TestStand COM对象的方法是确保所有变量不再包含对象 - 例如。 del()
他们,或将其设置为None
。或者,只要变量是函数局部变量,当函数结束时变量超出范围就会释放对象。
好吧,我在我的程序中遵循了这条规则,只要没有例外,我的程序就会正确释放对象。但是,如果我得到一个异常,那么我将从TestStand获得“对象未释放”消息。这似乎表明,当发生异常时,函数局部变量通常不会超出范围。
这是一个简化的代码示例:
class TestObject(object):
def __init__(self, name):
self.name = name
print("Init " + self.name)
def __del__(self):
print("Del " + self.name)
def test_func(parameter):
local_variable = parameter
try:
pass
# raise Exception("Test exception")
finally:
pass
# local_variable = None
# parameter = None
outer_object = TestObject('outer_object')
try:
inner_object = TestObject('inner_object')
try:
test_func(inner_object)
finally:
inner_object = None
finally:
outer_object = None
如图所示运行时,它显示了我的期望:
Init outer_object
Init inner_object
Del inner_object
Del outer_object
但如果我取消注释raise Exception...
行,我会得到:
Init outer_object
Init inner_object
Del outer_object
Traceback (most recent call last):
...
Exception: Test exception
Del inner_object
inner_object
因异常而被删除。
如果我取消注释将parameter
和local_variable
都设置为None
的行,那么我会得到我期望的结果:
Init outer_object
Init inner_object
Del inner_object
Del outer_object
Traceback (most recent call last):
...
Exception: Test exception
因此,当Python中发生异常时,函数局部变量究竟发生了什么?他们是否被保存在某处,所以他们不会像往常一样超出范围?控制这种行为的“正确方法”是什么?
答案 0 :(得分:2)
您的异常处理可能是通过保持对帧的引用来创建引用循环。正如the docs所说:
注意保持对框架的引用 对象,如第一个元素中所示 框架记录这些功能 return [[NB:“这些函数”在这里指的是 模块
inspect
中的一些,但其余部分 段落适用范围更广!]] ,可以导致你的程序 创建参考周期。一旦 参考周期已经创建了 可以是所有物体的寿命 从形成的对象访问 甚至可以使周期变得更长 如果Python的可选循环检测器是 启用。如果必须这样的循环 创建,重要的是要确保 它们明确地被打破以避免 物体的延迟破坏和 增加内存消耗 发生。虽然循环探测器会 抓住这些,破坏框架 (和局部变量)可以 通过消除循环确定性 一个finally
条款。这也是 如果循环探测器是重要的 编译Python时禁用 使用gc.disable()
。例如:
def handle_stackframe_without_leak():
frame = inspect.currentframe()
try:
# do something with the frame
finally:
del frame
答案 1 :(得分:1)
函数的范围适用于整个函数。请在finally
。
答案 2 :(得分:0)
根据this answer for another question,可以inspect local variables on the frame in an exception traceback通过tb_frame.f_locals
。因此,在异常处理期间,它看起来好像保持“活着”。