编程语言
首页 > 编程语言> > 异常期间的Python函数局部变量作用域

异常期间的Python函数局部变量作用域

作者:互联网

背景:我正在用Python对National Test’s 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
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

标签:scope,exception,exception-handling,python
来源: https://codeday.me/bug/20191210/2101001.html