2014-03-07 46 views
24

我在閱讀the source code of the incoming asyncio package。請注意,在該方法結束時,有一條self = None聲明。它有什麼作用?什麼是自我=無?

def _run(self): 
    try: 
     self._callback(*self._args) 
    except Exception as exc: 
     msg = 'Exception in callback {}{!r}'.format(self._callback, 
                self._args) 
     self._loop.call_exception_handler({ 
      'message': msg, 
      'exception': exc, 
      'handle': self, 
     }) 
    self = None # Needed to break cycles when an exception occurs. 

我認爲它會刪除實例,但下面的測試並不建議這樣:

class K: 
    def haha(self): 
     self = None 

a = K() 
a.haha() 
print(a) # a is still an instance 
+6

也許問題應該是「爲什麼可以將'self'設置爲'None'中斷週期?什麼週期?」 – satoru

回答

24

它簡單地清除本地參考self,確保如果出現異常參考傳遞給self._loop.call_exception_handler()是唯一剩餘的引用,並且沒有創建循環。

這仍然是需要的,因爲本地命名空間被異常追溯引用;它會而不是當功能退出時被清除,因爲還有對當地人的引用仍然存在。

這是記錄在sys.exc_info() function documentation一個警告:

警告:在被處理異常將導致循環引用的功能分配回溯返回值的局部變量。這將防止在同一個函數中由局部變量引用的任何東西,或者通過垃圾收集回溯。由於大多數函數不需要訪問回溯,所以最好的解決方案是使用類似於exctype, value = sys.exc_info()[:2]的東西來僅提取異常類型和值。如果您確實需要回溯,請確保在使用後刪除它(最好使用try ... finally語句),或者在不處理異常的函數中調用exc_info()

因爲tulip處理形成基本框架類的代碼通過從本地命名空間移除self代替,因爲它不能保證_callbackcall_exception_handler功能將清理它們的引用處理回溯循環引用情況。

在CPython中,對象在其引用計數降至0時被銷燬,但循環引用(引用自身的一系列對象將永遠不會看到它們的引用計數降爲0)。垃圾收集器確實試圖破壞這樣的週期,但它不能總是這樣做或不夠快。明確地清除引用可避免創建週期。

例如,如果有一個__del__方法,垃圾收集器將不會中斷一個循環,因爲它不知道在這種情況下以何種順序安全地中斷一個循環。

即使沒有__del__方法(框架類永遠不會假設情況不會如此),最好不要依賴垃圾回收器最終清除週期。

+1

你能舉一個這樣的循環引用的例子嗎? – msvalkon

+0

我還是不明白......我的意思是,如果命令'self = None'被命中,那麼很明顯該方法的結束會被觸發,在這種情況下,本地引用將會退出範圍,所以爲什麼我們需要'自我=無'? –

+0

@DerekChiang:不,因爲發生異常時存在實時追蹤,因此保持本地名稱空間處於活動狀態。 –

1

請注意,該行在Guido的revision 496中介紹。

在本次修訂中,對應於_run功能是run

def run(self): 
    try: 
     self._callback(*self._args) 
    except Exception: 
     tulip_log.exception('Exception in callback %s %r', 
          self._callback, self._args) 
    self = None # Needed to break cycles when an exception occurs. 

tulip_log僅僅是一個正常的記錄:logging.getLogger("tulip")

引擎蓋下,Logger.exception商店在LogRecordsys.exc_info()的結果,但exception通話後記錄對象不存在。

要驗證logging.exception不會引起參考週期,我做了以下實驗:

import time 

import logging 

class T: 
    def __del__(self): 
     print('T.__del__ called') 

    def test(self): 
     try: 
      1/0 
     except Exception: 
      logging.exception("Testing") 


def run(): 
    t = T() 
    t.test() 
    # t is supposed to be garbaged collected 


run() 

time.sleep(10) # to emulate a long running process 

這是結果:

$ python test.py 
ERROR:root:Testing 
Traceback (most recent call last): 
    File "test.py", line 11, in test 
    1/0 
ZeroDivisionError: integer division or modulo by zero 
T.__del__ called 

對象t是收集垃圾的預期。

所以,我不認爲self = None賦值是必要的。

+0

相同的提交在幾個位置添加'self = None',因爲對於框架來說這是很好的做法,並不是因爲代碼庫*在隔離*時顯示循環引用。 –

+0

@MartijnPieters當你生成循環引用時,你可以給我一個例子嗎?可能用'_callback'? – satoru

+0

我還沒有研究過鬱金香/ asyncio庫;我不知道設計意圖,也不知道你研究的是一個進行中的工作(即使當時還不是完整的庫)。 'logging'庫允許您註冊自定義處理程序和格式化程序;不要僅僅依靠默認的代碼庫。不幸的是,我現在沒有時間來構建一個ATM示例案例(這個評論來自智能手機通過3G連接提供)。 –