2010-01-13 55 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中處理。