python字典弱引用_Python 弱引用学习

来源: fireflow

链接:https://segmentfault.com/a/1190000005729873

和许多其它的高级语言一样,Python使用了垃圾回收器来自动销毁那些不再使用的对象。每个对象都有一个引用计数,当这个引用计数为0时Python能够安全地销毁这个对象。

引用计数会记录给定对象的引用个数,并在引用个数为零时收集该对象。由于一次仅能有一个对象被回收,引用计数无法回收循环引用的对象。

一组相互引用的对象若没有被其它对象直接引用,并且不可访问,则会永久存活下来。一个应用程序如果持续地产生这种不可访问的对象群组,就会发生内存泄漏。

在对象群组内部使用弱引用(即不会在引用计数中被计数的引用)有时能避免出现引用环,因此弱引用可用于解决循环引用的问题。

在计算机程序设计中,弱引用,与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则可能在任何时刻被回收。弱引用的主要作用就是减少循环引用,减少内存中不必要的对象存在的数量。

使用weakref模块,你可以创建到对象的弱引用,Python在对象的引用计数为0或只存在对象的弱引用时将回收这个对象。

创建弱引用

你可以通过调用weakref模块的ref(obj[,callback])来创建一个弱引用,obj是你想弱引用的对象,callback是一个可选的函数,当因没有引用导致Python要销毁这个对象时调用。回调函数callback要求单个参数(弱引用的对象)。

一旦你有了一个对象的弱引用,你就能通过调用弱引用来获取被弱引用的对象。

>>> 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) # 创建一个弱引用

>>> sys.getrefcount(o) # 引用计数并没有改变

2

>>> r

 # 弱引用所指向的对象信息

>>> o2 = r() # 获取弱引用所指向的对象

>>> o is o2

True

>>> sys.getrefcount(o)

3

>>> o = None

>>> o2 = None

>>> r # 当对象引用计数为零时,弱引用失效。

de>

上面的代码中,我们使用sys包中的getrefcount()来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。

一旦没有了对这个对象的其它的引用,调用弱引用将返回None,因为Python已经销毁了这个对象。 注意:大部分的对象不能通过弱引用来访问。

weakref模块中的getweakrefcount(obj)和getweakrefs(obj)分别返回弱引用数和关于所给对象的引用列表。

弱引用对于创建对象(这些对象很费资源)的缓存是有用的。

创建代理对象

代理对象是弱引用对象,它们的行为就像它们所引用的对象,这就便于你不必首先调用弱引用来访问背后的对象。通过weakref模块的proxy(obj[,callback])函数来创建代理对象。使用代理对象就如同使用对象本身一样:

import weakref

class Man:

def __init__(self,name):

self.name=name

def callback(self):

print 'callback'

o = Man('Jim')

p = weakref.proxy(o, callback)

p.test()

o = None

callback参数的目的和ref函数相同。在Python删除了一个引用的对象之后,使用代理将会导致一个weakref.ReferenceError错误。

循环引用

前面说过,使用弱引用,可以解决循环引用不能被垃圾回收的问题。

首先我们看下常规的循环引用,先创建一个简单的Graph类,然后创建三个Graph实例:

# -*- coding:utf-8 -*-

importweakref

importgc

frompprintimportpprint

classGraph(object):

def__init__(self,name):

self.name=name

self.other=None

defset_next(self,other):

print'%s.set_next(%r)'%(self.name,other)

self.other=other

defall_nodes(self):

yieldself

n=self.other

whilenandn.name!=self.name:

yieldn

n=n.other

ifnisself:

yieldn

return

def__str__(self):

return'->'.join(n.nameforninself.all_nodes())

def__repr__(self):

return''%(self.__class__.__name__,id(self),self.name)

def__del__(self):

print'(Deleting %s)'%self.name

defcollect_and_show_garbage():

print'Collecting...'

n=gc.collect()

print'unreachable objects:',n

print'garbage:',

pprint(gc.garbage)

defdemo(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:'

printstr(one)

collect_and_show_garbage()

print

three=None

two=None

print'After 2 references removed'

printstr(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)

whilegc.garbage:

delgc.garbage[0]

printcollect_and_show_garbage()

这里使用了python的gc库的几个方法, 解释如下:

gc.collect() 收集垃圾

gc.garbage 获取垃圾列表

gc.set_debug(gc.DBEUG_LEAK) 打印无法看到的对象信息

运行结果如下:

Setting up the cycle

Setupgraph:

one.set_next()

two.set_next()

three.set_next()

Graph:

one->two->three->one

Collecting...

unreachableobjects:g0

garbage:[]

After2references removed

one->two->three->one

Collecting...

unreachableobjects:0

garbage:[]

removeing last reference

Collecting...

unreachableobjects:6

garbage:[,

,

,

{'name':'one','other': },

{'name':'two','other': },

{'name':'three','other': }]

breaking the cycleandcleaning up garbage

one.set_next(None)

(Deletingtwo)

(Deletingthree)

(Deletingone)

Collecting...

unreachableobjects:0

garbage:[]

None

[Finishedin0.4s]c:uncollectable

gc:uncollectable

gc:uncollectable

gc:uncollectable

gc:uncollectable

gc:uncollectable

从结果中我们可以看出,即使我们删除了Graph实例的本地引用,它依然存在垃圾列表中,不能回收。

接下来创建使弱引用的WeakGraph类:

classWeakGraph(Graph):

defset_next(self,other):

ifotherisnotNone:

ifselfinother.all_nodes():

other=weakref.proxy(other)

super(WeakGraph,self).set_next(other)

return

demo(WeakGraph)

结果如下:

Setting up the cycle

Setupgraph:

one.set_next()

two.set_next()

three.set_next()

Graph:

one->two->three

Collecting...

unreachableobjects:Traceback(most recent calllast):

File'D:\apps\platform\demo\demo.py',line87,in

gc.garbage[0].set_next(None)

IndexError:listindex out of range

0

garbage:[]

After2references removed

one->two->three

Collecting...

unreachableobjects:0

garbage:[]

removeing last reference

(Deletingone)

(Deletingtwo)

(Deletingthree)

Collecting...

unreachableobjects:0

garbage:[]

breaking the cycleandcleaning upgarbage

[Finishedin0.4swithexit code1]

上面的类中,使用代理来指示已看到的对象,随着demo()删除了对象的所有本地引用,循环会断开,这样垃圾回收期就可以将这些对象删除。

因此我们我们在实际工作中如果需要用到循环引用的话,尽量采用弱引用来实现。

缓存对象

ref和proxy都只可用与维护单个对象的弱引用,如果想同时创建多个对象的弱引用咋办?这时可以使用WeakKeyDictionary和WeakValueDictionary来实现。

WeakValueDictionary类,顾名思义,本质上还是个字典类型,只是它的值类型是弱引用。当这些值引用的对象不再被其他非弱引用对象引用时,那么这些引用的对象就可以通过垃圾回收器进行回收。

下面的例子说明了常规字典与WeakValueDictionary的区别。

# -*- coding:utf-8 -*-

importweakref

importgc

frompprintimportpprint

gc.set_debug(gc.DEBUG_LEAK)

classMan(object):

def__init__(self,name):

self.name=name

def__repr__(self):

return''%self.name

def__del__(self):

print'deleting %s'%self

defdemo(cache_factory):

all_refs={}

print'cache type:',cache_factory

cache=cache_factory()

fornamein['Jim','Tom','Green']:

man=Man(name)

cache[name]=man

all_refs[name]=man

delman

print'all_refs=',

pprint(all_refs)

print

print'before, cache contains:',cache.keys()

forname,valueincache.items():

print'%s = %s'%(name,value)

print'\ncleanup'

delall_refs

gc.collect()

print

print'after, cache contains:',cache.keys()

forname,valueincache.items():

print'%s = %s'%(name,value)

print'demo returning'

return

demo(dict)

print

demo(weakref.WeakValueDictionary)

结果如下所示:

cache type:

all_refs={'Green': ,'Jim': ,'Tom': }

before,cachecontains:['Jim','Green','Tom']

Jim=

Green=

Tom=

cleanup

after,cachecontains:['Jim','Green','Tom']

Jim=

Green=

Tom=

demo returning

deleting

deleting

deleting

cache type:weakref.WeakValueDictionary

all_refs={'Green': ,'Jim': ,'Tom': }

before,cachecontains:['Jim','Green','Tom']

Jim=

Green=

Tom=

cleanup

deleting

deleting

after,cachecontains:[]

demoreturning

[Finishedin0.3s]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值