异常期间的Python函数局部变量范围

标签 python exception scope

背景:我正在用 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 由于异常被延迟删除。

如果我取消注释将 parameterlocal_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 a finally clause. This is also important if the cycle detector was disabled when Python was compiled or using gc.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/

相关文章:

python - 在 wagtail 的 draftail 中包含显示代码和引号的元素

python - 使用 ID 列表进行过滤时,如何从 Django 查询中获取有限的结果?

java - 异常设计与应用需求不匹配

c++ - 将字符串转换为整数、 double 、 float 而无需捕获异常

php - PHP 中的 session 间变量

python - 为什么我的函数无法访问封闭函数中的变量?

c++ - 在此范围内未声明错误

python - 当用户登录时如何获取用户对象,否则如何获取用户对象?

python - Pandas - Case when & default in pandas

python - 使用gevent访问外部http服务时出现间歇性问题