Python 弱引用 學習

發表於2016-06-17

參考
1.weakref – Garbage-collectable references to objects
2.Python弱引用介紹
和許多其它的高階語言一樣,Python使用了垃圾回收器來自動銷燬那些不再使用的物件。每個物件都有一個引用計數,當這個引用計數為0時Python能夠安全地銷燬這個物件。

引用計數會記錄給定物件的引用個數,並在引用個數為零時收集該物件。由於一次僅能有一個物件被回收,引用計數無法回收迴圈引用的物件。

一組相互引用的物件若沒有被其它物件直接引用,並且不可訪問,則會永久存活下來。一個應用程式如果持續地產生這種不可訪問的物件群組,就會發生記憶體洩漏。

在物件群組內部使用弱引用(即不會在引用計數中被計數的引用)有時能避免出現引用環,因此弱引用可用於解決迴圈引用的問題。

在計算機程式設計中,弱引用,與強引用相對,是指不能確保其引用的物件不會被垃圾回收器回收的引用。一個物件若只被弱引用所引用,則可能在任何時刻被回收。弱引用的主要作用就是減少迴圈引用,減少記憶體中不必要的物件存在的數量。

使用weakref模組,你可以建立到物件的弱引用,Python在物件的引用計數為0或只存在物件的弱引用時將回收這個物件。

建立弱引用

你可以通過呼叫weakref模組的ref(obj[,callback])來建立一個弱引用,obj是你想弱引用的物件,callback是一個可選的函式,當因沒有引用導致Python要銷燬這個物件時呼叫。回撥函式callback要求單個引數(弱引用的物件)。

一旦你有了一個物件的弱引用,你就能通過呼叫弱引用來獲取被弱引用的物件。

上面的程式碼中,我們使用sys包中的getrefcount()來檢視某個物件的引用計數。需要注意的是,當使用某個引用作為引數,傳遞給getrefcount()時,引數實際上建立了一個臨時的引用。因此,getrefcount()所得到的結果,會比期望的多1。

一旦沒有了對這個物件的其它的引用,呼叫弱引用將返回None,因為Python已經銷燬了這個物件。 注意:大部分的物件不能通過弱引用來訪問。

weakref模組中的getweakrefcount(obj)和getweakrefs(obj)分別返回弱引用數和關於所給物件的引用列表。

弱引用對於建立物件(這些物件很費資源)的快取是有用的。

建立代理物件

代理物件是弱引用物件,它們的行為就像它們所引用的物件,這就便於你不必首先呼叫弱引用來訪問背後的物件。通過weakref模組的proxy(obj[,callback])函式來建立代理物件。使用代理物件就如同使用物件本身一樣:

callback引數的目的和ref函式相同。在Python刪除了一個引用的物件之後,使用代理將會導致一個weakref.ReferenceError錯誤。

迴圈引用

前面說過,使用弱引用,可以解決迴圈引用不能被垃圾回收的問題。
首先我們看下常規的迴圈引用,先建立一個簡單的Graph類,然後建立三個Graph例項:

這裡使用了python的gc庫的幾個方法, 解釋如下:

  • gc.collect() 收集垃圾
  • gc.garbage 獲取垃圾列表
  • gc.set_debug(gc.DBEUG_LEAK) 列印無法看到的物件資訊

執行結果如下:

從結果中我們可以看出,即使我們刪除了Graph例項的本地引用,它依然存在垃圾列表中,不能回收。
接下來建立使弱引用的WeakGraph類:

結果如下:

上面的類中,使用代理來指示已看到的物件,隨著demo()刪除了物件的所有本地引用,迴圈會斷開,這樣垃圾回收期就可以將這些物件刪除。

因此我們我們在實際工作中如果需要用到迴圈引用的話,儘量採用弱引用來實現。

快取物件

refproxy都只可用與維護單個物件的弱引用,如果想同時建立多個物件的弱引用咋辦?這時可以使用WeakKeyDictionaryWeakValueDictionary來實現。

WeakValueDictionary類,顧名思義,本質上還是個字典型別,只是它的值型別是弱引用。當這些值引用的物件不再被其他非弱引用物件引用時,那麼這些引用的物件就可以通過垃圾回收器進行回收。
下面的例子說明了常規字典與WeakValueDictionary的區別。

結果如下所示:

相關文章