摘要:引用計數(shù)會記錄給定對象的引用個數(shù),并在引用個數(shù)為零時收集該對象。在對象群組內(nèi)部使用弱引用即不會在引用計數(shù)中被計數(shù)的引用有時能避免出現(xiàn)引用環(huán),因此弱引用可用于解決循環(huán)引用的問題。
參考
1.weakref – Garbage-collectable references to objects
2.Python弱引用介紹
和許多其它的高級語言一樣,Python使用了垃圾回收器來自動銷毀那些不再使用的對象。每個對象都有一個引用計數(shù),當這個引用計數(shù)為0時Python能夠安全地銷毀這個對象。
引用計數(shù)會記錄給定對象的引用個數(shù),并在引用個數(shù)為零時收集該對象。由于一次僅能有一個對象被回收,引用計數(shù)無法回收循環(huán)引用的對象。
一組相互引用的對象若沒有被其它對象直接引用,并且不可訪問,則會永久存活下來。一個應用程序如果持續(xù)地產(chǎn)生這種不可訪問的對象群組,就會發(fā)生內(nèi)存泄漏。
在對象群組內(nèi)部使用弱引用(即不會在引用計數(shù)中被計數(shù)的引用)有時能避免出現(xiàn)引用環(huán),因此弱引用可用于解決循環(huán)引用的問題。
在計算機程序設計中,弱引用,與強引用相對,是指不能確保其引用的對象不會被垃圾回收器回收的引用。一個對象若只被弱引用所引用,則可能在任何時刻被回收。弱引用的主要作用就是減少循環(huán)引用,減少內(nèi)存中不必要的對象存在的數(shù)量。
使用weakref模塊,你可以創(chuàng)建到對象的弱引用,Python在對象的引用計數(shù)為0或只存在對象的弱引用時將回收這個對象。
創(chuàng)建弱引用你可以通過調(diào)用weakref模塊的ref(obj[,callback])來創(chuàng)建一個弱引用,obj是你想弱引用的對象,callback是一個可選的函數(shù),當因沒有引用導致Python要銷毀這個對象時調(diào)用?;卣{(diào)函數(shù)callback要求單個參數(shù)(弱引用的對象)。
一旦你有了一個對象的弱引用,你就能通過調(diào)用弱引用來獲取被弱引用的對象。
>>>> import sys >>> import weakref >>> class Man: def __init__(self,name): print self.name = name >>> o = Man("Jim") >>> sys.getrefcount(o) 2 >>> r = weakref.ref(o) # 創(chuàng)建一個弱引用 >>> sys.getrefcount(o) # 引用計數(shù)并沒有改變 2 >>> r# 弱引用所指向的對象信息 >>> o2 = r() # 獲取弱引用所指向的對象 >>> o is o2 True >>> sys.getrefcount(o) 3 >>> o = None >>> o2 = None >>> r # 當對象引用計數(shù)為零時,弱引用失效。 de>
上面的代碼中,我們使用sys包中的getrefcount()來查看某個對象的引用計數(shù)。需要注意的是,當使用某個引用作為參數(shù),傳遞給getrefcount()時,參數(shù)實際上創(chuàng)建了一個臨時的引用。因此,getrefcount()所得到的結(jié)果,會比期望的多1。
一旦沒有了對這個對象的其它的引用,調(diào)用弱引用將返回None,因為Python已經(jīng)銷毀了這個對象。 注意:大部分的對象不能通過弱引用來訪問。
weakref模塊中的getweakrefcount(obj)和getweakrefs(obj)分別返回弱引用數(shù)和關于所給對象的引用列表。
弱引用對于創(chuàng)建對象(這些對象很費資源)的緩存是有用的。
創(chuàng)建代理對象代理對象是弱引用對象,它們的行為就像它們所引用的對象,這就便于你不必首先調(diào)用弱引用來訪問背后的對象。通過weakref模塊的proxy(obj[,callback])函數(shù)來創(chuàng)建代理對象。使用代理對象就如同使用對象本身一樣:
import weakref class Man: def __init__(self, name): self.name = name def test(self): print "this is a test!" def callback(self): print "callback" o = Man("Jim") p = weakref.proxy(o, callback) p.test() o=None p.test()
callback參數(shù)的作用和ref函數(shù)中callback一樣。在Python刪除了一個引用的對象之后,使用代理將會導致一個weakref.ReferenceError錯誤。
循環(huán)引用前面說過,使用弱引用,可以解決循環(huán)引用不能被垃圾回收的問題。
首先我們看下常規(guī)的循環(huán)引用,先創(chuàng)建一個簡單的Graph類,然后創(chuàng)建三個Graph實例:
# -*- coding:utf-8 -*- import weakref import gc from pprint import pprint class Graph(object): def __init__(self, name): self.name = name self.other = None def set_next(self, other): print "%s.set_next(%r)" % (self.name, other) self.other = other def all_nodes(self): yield self n = self.other while n and n.name !=self.name: yield n n = n.other if n is self: yield n return def __str__(self): return "->".join(n.name for n in self.all_nodes()) def __repr__(self): return "<%s at 0x%x name=%s>" % (self.__class__.__name__, id(self), self.name) def __del__(self): print "(Deleting %s)" % self.name def collect_and_show_garbage(): print "Collecting..." n = gc.collect() print "unreachable objects:", n print "garbage:", pprint(gc.garbage) def demo(graph_factory): print "Set up graph:" one = graph_factory("one") two = graph_factory("two") three = graph_factory("three") one.set_next(two) two.set_next(three) three.set_next(one) print print "Graph:" print str(one) collect_and_show_garbage() print three = None two = None print "After 2 references removed" print str(one) collect_and_show_garbage() print print "removeing last reference" one = None collect_and_show_garbage() gc.set_debug(gc.DEBUG_LEAK) print "Setting up the cycle" print demo(Graph) print print "breaking the cycle and cleaning up garbage" print gc.garbage[0].set_next(None) while gc.garbage: del gc.garbage[0] print collect_and_show_garbage()
這里使用了python的gc庫的幾個方法, 解釋如下:
gc.collect() 收集垃圾
gc.garbage 獲取垃圾列表
gc.set_debug(gc.DBEUG_LEAK) 打印無法看到的對象信息
運行結(jié)果如下:
Setting up the cycle Set up graph: one.set_next() two.set_next( ) three.set_next( ) Graph: one->two->three->one Collecting... unreachable objects:g 0 garbage:[] After 2 references removed one->two->three->one Collecting... unreachable objects: 0 garbage:[] removeing last reference Collecting... unreachable objects: 6 garbage:[ , , , {"name": "one", "other": }, {"name": "two", "other": }, {"name": "three", "other": }] breaking the cycle and cleaning up garbage one.set_next(None) (Deleting two) (Deleting three) (Deleting one) Collecting... unreachable objects: 0 garbage:[] None [Finished in 0.4s]c: uncollectable gc: uncollectable gc: uncollectable gc: uncollectable gc: uncollectable gc: uncollectable
從結(jié)果中我們可以看出,即使我們刪除了Graph實例的本地引用,它依然存在垃圾列表中,不能回收。
接下來創(chuàng)建使弱引用的WeakGraph類:
class WeakGraph(Graph): def set_next(self, other): if other is not None: if self in other.all_nodes(): other = weakref.proxy(other) super(WeakGraph, self).set_next(other) return demo(WeakGraph)
結(jié)果如下:
Setting up the cycle Set up graph: one.set_next() two.set_next( ) three.set_next( ) Graph: one->two->three Collecting... unreachable objects:Traceback (most recent call last): File "D:appsplatformdemodemo.py", line 87, in gc.garbage[0].set_next(None) IndexError: list index out of range 0 garbage:[] After 2 references removed one->two->three Collecting... unreachable objects: 0 garbage:[] removeing last reference (Deleting one) (Deleting two) (Deleting three) Collecting... unreachable objects: 0 garbage:[] breaking the cycle and cleaning up garbage [Finished in 0.4s with exit code 1]
上面的類中,使用代理來指示已看到的對象,隨著demo()刪除了對象的所有本地引用,循環(huán)會斷開,這樣垃圾回收期就可以將這些對象刪除。
因此我們我們在實際工作中如果需要用到循環(huán)引用的話,盡量采用弱引用來實現(xiàn)。
緩存對象ref和proxy都只可用與維護單個對象的弱引用,如果想同時創(chuàng)建多個對象的弱引用咋辦?這時可以使用WeakKeyDictionary和WeakValueDictionary來實現(xiàn)。
WeakValueDictionary類,顧名思義,本質(zhì)上還是個字典類型,只是它的值類型是弱引用。當這些值引用的對象不再被其他非弱引用對象引用時,那么這些引用的對象就可以通過垃圾回收器進行回收。
下面的例子說明了常規(guī)字典與WeakValueDictionary的區(qū)別。
# -*- coding:utf-8 -*- import weakref import gc from pprint import pprint gc.set_debug(gc.DEBUG_LEAK) class Man(object): def __init__(self, name): self.name = name def __repr__(self): return "" % self.name def __del__(self): print "deleting %s" % self def demo(cache_factory): all_refs = {} print "cache type:", cache_factory cache = cache_factory() for name in ["Jim", "Tom", "Green"]: man = Man(name) cache[name] = man all_refs[name] = man del man print "all_refs=", pprint(all_refs) print print "before, cache contains:", cache.keys() for name, value in cache.items(): print "%s = %s" % (name, value) print " cleanup" del all_refs gc.collect() print print "after, cache contains:", cache.keys() for name, value in cache.items(): print "%s = %s" % (name, value) print "demo returning" return demo(dict) print demo(weakref.WeakValueDictionary)
結(jié)果如下所示:
cache type:all_refs={"Green": , "Jim": , "Tom": } before, cache contains: ["Jim", "Green", "Tom"] Jim = Green = Tom = cleanup after, cache contains: ["Jim", "Green", "Tom"] Jim = Green = Tom = demo returning deleting deleting deleting cache type: weakref.WeakValueDictionary all_refs={"Green": , "Jim": , "Tom": } before, cache contains: ["Jim", "Green", "Tom"] Jim = Green = Tom = cleanup deleting deleting after, cache contains: [] demo returning [Finished in 0.3s]
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/37994.html
摘要:第一次初始化對象,并且用變量來引用,所以這里的引用計數(shù)就為。接下來通過創(chuàng)建一個弱引用,通過打印引用計數(shù)后,發(fā)現(xiàn)計數(shù)并沒有改變。由于一次僅能有一個對象被回收,引用計數(shù)無法回收循環(huán)引用的對象。所以弱引用很適合處理這種循環(huán)引用的場景。 寫在前邊: 之前的socket系列就告一段落,主要是對自己所學做一個總結(jié)與記錄。 接下來我打算基于libevent寫一個支持并發(fā)的HTTP服務器。因為之前學習...
摘要:函數(shù)的參數(shù)作為引用時唯一支持的參數(shù)傳遞模式是共享傳參,它指函數(shù)的形參獲得實參中各個引用的副本,即形參是實參的別名。而在上面這個例子中,類的屬性實際上是形參所指向的對象所指對象,的別名。 《流暢的Python》筆記本篇是面向?qū)ο髴T用方法的第一篇,一共六篇。本篇主要是一些概念性的討論,內(nèi)容有:Python中的變量,對象標識,值,別名,元組的某些特性,深淺復制,引用,函數(shù)參數(shù),垃圾回收,de...
摘要:一對象引用基礎知識變量是標注而不是容器。也就是說元組中不可變的是元素的標識,但元組的值會隨著引用的可變對象變化而變化。在中每個對象的引用都會有統(tǒng)計。弱引用不會妨礙對象被當做垃圾回收。 導語:本文章記錄了本人在學習Python基礎之面向?qū)ο笃闹攸c知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、明確變量保存的是引用這一本質(zhì);2、熟悉對象引用的基礎知識;...
摘要:運算符比較兩個對象的標識函數(shù)返回對象標識的整數(shù)表示。實際上,每個對象都會統(tǒng)計有多少引用指向自己。對象被銷毀了,調(diào)用了回調(diào),的值變成了。當對象的引用數(shù)量歸零后,垃圾回收程序會把對象銷毀。引用的目標對象稱為所指對象。 對象不是個盒子 showImg(https://segmentfault.com/img/bV95mW?w=1784&h=988); class Gizmo: def...
摘要:自定義向量類型從自定義向量類型入手寫出符合風格的對象,這離不開特殊方法的支持。將對象定為不可變的通過使用兩個前導下劃線。程序員約定使用一個下劃線前綴編寫受保護的屬性即,他們認為應該使用命名約定來避免意外覆蓋屬性。 導語:本文章記錄了本人在學習Python基礎之面向?qū)ο笃闹攸c知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、掌握編寫Pythonic c...
閱讀 3101·2021-10-13 09:40
閱讀 3954·2021-09-22 15:51
閱讀 1501·2021-09-22 15:48
閱讀 1068·2021-09-06 15:00
閱讀 1793·2019-08-30 15:43
閱讀 2363·2019-08-29 18:35
閱讀 1674·2019-08-29 16:18
閱讀 3618·2019-08-29 12:49