2010-01-13 96 views
3

背景:我正在使用Python编写National Instruments TestStand的COM编程。如果对象没有正确“释放”(TestStand会弹出一个“对象未正确释放”的调试对话框)。在Python中释放TestStand COM对象的方法是确保所有变量不再包含对象 - 例如。 del()他们,或将它们设置为None。或者,只要变量是函数局部变量,只要函数结束时变量超出范围,对象就会被释放。例外情况下的Python函数局部变量范围

嗯,我在我的程序中遵循了这个规则,只要没有例外,我的程序就会正确释放对象。但是,如果我得到一个异常,那么我得到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_variableNone行,然后我得到了我期望:

Init outer_object 
Init inner_object 
Del inner_object 
Del outer_object 
Traceback (most recent call last): 
... 
Exception: Test exception 

所以当异常在Python中发生的,到底发生了什么起作用局部变量?他们是否被保存在某个地方,这样他们就不会像往常那样超出范围?什么是控制这种行为的“正确方法”?

回答

2

您的异常处理可能通过保持对帧的引用来创建引用循环。作为the docs所言:

注意饲养的参考帧 对象,在帧的第一个元素 发现记录这些功能 回报[注:“这些功能”在这里是指 一些在模块inspect中,但其余的 段适用范围更广!]],可以导致你的程序创建参考周期为 。一旦创建了 参考周期,如果启用了Python的可选周期检测器 ,则可以从形成 循环的对象访问的所有对象的所有对象的生命周期可能变得更长,即使 也是如此。如果创建这样的周期必须为 ,确保 它们被明确打破以避免 对象的延迟破坏和 发生的增加的内存消耗是很重要的。虽然周期检测器将捕获这些数据,但通过删除 a finally子句中的周期,可以确定对帧 (和局部变量)的破坏是确定性的。如果循环检测器在编译Python时禁用了 ,或者在使用gc.disable()时为 ,这也是 。例如:

def handle_stackframe_without_leak(): 
    frame = inspect.currentframe() 
    try: 
     # do something with the frame 
    finally: 
     del frame 
+0

谢谢。我不太清楚我的理解“您的异常处理可能通过保持对帧的引用来创建引用循环。”所以我添加了一个代码示例。我很想知道你对它的看法。 – 2010-01-14 00:07:24

+0

@Craig,没有引用循环,但仍然比您想象的更长寿命的引用,因为只要异常处理(当您设置为()时,栈中的所有框架(以及对象的引用)没有所有这些引用,那么即使帧仍然存在,对象也可以被删除)。 – 2010-01-14 00:33:11

1

函数的作用域是针对整个函数的。在finally中处理。