2009-11-01 38 views
11

我在不同的代碼庫中看到過,只是在PyMOTW上閱讀(請參閱第一個註釋here)。爲什麼需要顯式刪除sys.exc_info()回溯?

該解釋說,如果回溯被分配給來自sys.exc_info()[2]的變量,將創建一個循環,但爲什麼?

這個問題有多大?我是否應該在我的代碼庫中搜索exc_info的所有用途,並確保回溯被刪除?

回答

19

的Python 3(更新原來的答案):

在Python 3,在這個問題引述的意見已經從Python文檔中刪除。我的原始答案(後面)僅適用於在其文檔中包含引號的Python版本。

的Python 2:

Python的垃圾回收器會,最終,找到並刪除喜歡從內部棧幀本身的一個指的是回溯堆棧中創建的一個循環引用,所以不要去返回並重寫你的代碼。但是,展望未來,你可以遵循的

http://docs.python.org/library/sys.html

的建議(它記錄了exc_info())說:

exctype, value = sys.exc_info()[:2] 

當你需要抓住的例外。

另外兩個想法:

首先,你爲什麼運行exc_info()呢?

如果你想趕上異常應不乾脆說:

try: 
    ... 
except Exception as e: # or "Exception, e" in old Pythons 
    ... do with with e ... 

,而不是擺弄的sys模塊內的對象?

第二:好的,我提出了很多建議,但並沒有真正回答你的問題。:-)

爲什麼創建一個循環?那麼,在簡單的情況下,創建了一個週期時的對象指的是本身:

a = [1,2,3] 
a.append(a) 

或者當兩個對象相互引用:

a = [1,2,3] 
b = [4,5,a] 
a.append(b) 

在這兩種情況下,該函數結束時變量值將仍然存在,因爲它們被鎖定在一個引用計數的擁抱中:在另一個首先消失之前,它們都不會消失!只有現代的Python垃圾收集器才能解決這個問題,最終注意到循環並打破它。

所以理解這種情況的關鍵在於一個「追溯」對象 - 第三件事(在索引#2處)由exc_info()返回 - 包含每個函數的「棧幀」,當調用異常時該函數處於活動狀態。並且這些堆棧幀是而不是「dead」對象,顯示調用execption時;幀仍然活着!捕捉到異常的函數仍然存在,所以它的堆棧框架是一個有生命的東西,它仍然在不斷增長並且在其代碼執行時會丟失變量引用以處理異常(並且在完成「except」子句時執行其他操作關於它的工作)。

所以當你說t = sys.exc_info()[2]時,traceback中的那些堆棧幀之一 - 實際上屬於當前正在運行的函數的幀 - 現在有一個名爲t的變量,它指向堆棧幀本身,就像我上面展示的那樣創建一個循環。

+1

[警告](https://docs.python.org/3.2/library/sys.html#sys.exc_info)您提到的使用'[:2]'的建議'已從文檔中刪除,因爲Python 3.3。原因在[#7340](https://bugs.python.org/issue7340)中解釋。 '[:2]'技巧不再有效,''處理的異常的__traceback__'屬性應該被設置爲'None'。相關的PEP:[344](https://www.python.org/dev/peps/pep-0344/#open-issue-garbage-collection),[3134](https://www.python.org/dev/peps/pep-3134 /#open-issue-garbage-collection),[3110](https://www.python.org/dev/peps/pep-3110/#semantic-changes)。 – Delgan 2017-12-09 10:56:11

10

追蹤包含對所有活動框架的引用,這些框架又包含對這些各種框架中所有局部變量的引用 - 這些引用是追蹤和框架對象的大部分工作,所以這並不令人驚訝。所以,如果你添加一個引用回溯回溯(或者暫時添加它時沒有及時刪除它),你不可避免地會形成一個很大的引用循環 - 這會干擾垃圾回收(如果有任何對象會干擾它)在循環中屬於覆蓋__del__的類,即終結器方法)。

特別是在一個長期運行的程序中,干擾垃圾收集並不是最好的想法,因爲你將會記住你並不真正需要的東西(超過必要的時間,或者無限期地基本上通過讓這些循環包含具有終結器的對象來阻止垃圾回收)。

所以,最好儘快擺脫追溯,無論它們是否來自exc_info