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

时间:2010-01-13 09:30:22

标签: python exception exception-handling 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中发生异常时,函数局部变量究竟发生了什么?他们是否被保存在某处,所以他们不会像往常一样超出范围?控制这种行为的“正确方法”是什么?

3 个答案:

答案 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。因此,在异常处理期间,它看起来好像保持“活着”。