背景:我正在用 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 中发生异常时,函数局部变量到底发生了什么?它们是否被保存在某个地方,所以它们不会像往常一样超出范围?控制这种行为的“正确方法”是什么?
最佳答案
您的异常处理可能通过保留对帧的引用来创建引用循环。作为the docs把它:
Note Keeping references to frame objects, as found in the first element of the frame records these functions return [[NB: "these functions" here refers to some in module
inspect
, but the rest of the paragraph applies more widely!]], can cause your program to create reference cycles. Once a reference cycle has been created, the lifespan of all objects which can be accessed from the objects which form the cycle can become much longer even if Python’s optional cycle detector is enabled. If such cycles must be created, it is important to ensure they are explicitly broken to avoid the delayed destruction of objects and increased memory consumption which occurs. Though the cycle detector will catch these, destruction of the frames (and local variables) can be made deterministic by removing the cycle in afinally
clause. This is also important if the cycle detector was disabled when Python was compiled or usinggc.disable()
. For example:
def handle_stackframe_without_leak():
frame = inspect.currentframe()
try:
# do something with the frame
finally:
del frame
关于异常期间的Python函数局部变量范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2055611/