python 什么时候使用 weakref
发布网友
发布时间:2022-04-24 09:51
我来回答
共2个回答
懂视网
时间:2022-05-11 01:48
在【python 标准库】中看到的一段代码,非常有帮助:
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
首尾的2处yield均只返回一次,作为循环图的起点、终点,而n作为图可能的节点,每次在next调用中均返回next节点
利用这个迭代器,就可以轻松打印出图的结构:
def __str__(self):
return '->'.join((n.name for n in self.all_nodes()))
Graph:
one->two->three->one
实现一个图结构需要利用python里面的弱引用,
我们先看一下标准的向图结构中增加下一节点的代码:
def set_next(self, other):
print '%s.next %r' % ( self.name, other)
self.other = other
这样绑定后,在属性字段中,增加一个对于下一节点的引用
c.__dict__
{'other': , 'name': '1'}
所以,即使手动调用了 a = None, b = None, c = None,对象也不会被删除
Garbage:[,
,
,
{'name': 'one', 'other': },
{'name': 'two', 'other': },
{'name': 'three', 'other': }]
而弱引用是指“引用一个对象,但并不增加被引用对象的指针计数”
可以通过c = weekref.ref(k,func)
来指定引用的对象及对象删除后的动作func
调用时,使用c() 来引用k
但是在上个例子里面,我们需要一个“代理对象”来代理这个被引用的对象,从而使set_next 函数对于变量other可以同正常变量一样使用
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
从而避免了通过other()来引用一个other对象~
热心网友
时间:2022-05-10 22:56
和许多其它的高级语言一样,Python使用了垃圾回收器来自动销毁那些不再使用的对象。每个对象都有一个引用计数,当这个引用计数为0时Python能够安全地销毁这个对象。
使用weakref模块,你可以创建到对象的弱引用,Python在对象的引用计数为0或只存在对象的弱引用时将回收这个对象。
一、 创建弱引用
你可以通过调用weakref模块的ref(obj[,callback])来创建一个弱引用,obj是你想弱引用的对象,callback是一个可选的函数,当因没有引用导致Python要销毁这个对象时调用。回调函数callback要求单个参数(弱引用的对象)。
一旦你有了一个对象的弱引用,你就能通过调用弱引用来获取被弱引用的对象。下面的例子创建了一个对socket对象的弱引用:
>>> from socket import *
>>> import weakref
>>> s=socket(AF_INET,SOCK_STREAM)
>>> ref=weakref.ref(s)
>>> s
<socket._socketobject instance at 007B4A94>
>>> ref
<weakref at 0x81195c; to 'instance' at 0x7b4a94>
>>> ref() #调用它来访问被引用的对象
<socket.socketobject instance at 007B4A94>
一旦没有了对这个对象的其它的引用,调用弱引用将返回None,因为Python已经销毁了这个对象。 注意:大部分的对象不能通过弱引用来访问。
weakref模块中的getweakrefcount(obj)和getweakrefs(obj)分别返回弱引用数和关于所给对象的引用列表。
弱引用对于创建对象(这些对象很费资源)的缓存是有用的。
二、创建代理对象
代理对象是弱引用对象,它们的行为就像它们所引用的对象,这就便于你不必首先调用弱引用来访问背后的对象。通过weakref模块的proxy(obj[,callback])函数来创建代理对象。使用代理对象就如同使用对象本身一样:
>>> from socket import*
>>> import weakref
>>> s=socket(AF_INET,SOCK_STREAM)
>>> ref=weakref.proxy(s)
>>> s
<socket._socketobject instance at 007E4874>
>>> ref
<socket._socketobject instance at 007E4874>
>>> ref.close() #对象的方法同样工作
callback参数的目的和ref函数相同。在Python删除了一个引用的对象之后,使用代理将会导致一个weakref.ReferenceError错误:
>>> def s
>>> ref
Traceback (most recent call last):
File "<stdin>", line 1, in ?
弱引用使用的机会不是很多,一般用来进行 cache 编程。我们可以使用 weakref.ref() 来创建一个弱引用。
>>>> import sys
>>> import weakref
>>> class Class1:
def test(self):
print "test..."
>>> o = Class1()
>>> sys.getrefcount(o)
2
>>> r = weakref.ref(o) # 创建一个弱引用
>>> sys.getrefcount(o) # 引用计数并没有改变
2
>>> r
<weakref at 00D3B3F0; to 'instance' at 00D37A30> # 弱引用所指向的对象信息
>>> o2 = r() # 获取弱引用所指向的对象
>>> o is o2
True
>>> sys.getrefcount(o)
3
>>> o = None
>>> o2 = None
>>> r # 当对象引用计数为零时,弱引用失效。
<weakref at 00D3B3F0; dead>de>
weakref 还提供了一个 proxy():
import sys
import weakref
class Class1:
def test(self):
print "test"
def callback(self):
print "callback"
o = Class1()
p = weakref.proxy(o, callback)
p.test()
o = None
python的弱引用指引用一个对象但不增加它的引用计数器。这么做的好处是什么呢?什么时候需要考虑用若引用呢?
假设我们在设计一个游戏,有一个角色类Char,我们要给他添加一个效果(比如中毒),于是设计了一个效果类Effect。现在,给角色增加效果看上去就像这样:
char.effect=Effect()# 给角色添加一个效果
每个效果生效的时机都是不同的,为了方便复用,我们再设计一个激活策略类ActivePloy,负责激活效果。于是在Effect和ActivePloy的内部看上去就像这样:
classEffect(object):
def__init__(self):
self.active_ploy=ActivePloy(self)
defactive(self):
"""激活时的处理"""
pass
classActivePloy(object):
def__init__(self,effect):
self.effect=effect
defactive(self):
"""激活时,激活对应效果"""
self.effect.active()
这样做的好处是Effect不用关心自己何时激活,激活的判断都放给ActivePloy来处理。看上去挺好的,但是,这里面有一个问题,就是当我们试图给玩家去掉这个效果时……
delchar.effect
仔细想想,这么干以后,Effect的实例其实是没有被回收的,因为Effect和ActivePloy交叉引用,他们的引用计数都为1。
那么我们为了干净的删除effect,似乎就只能手动的来清理一下他们之间的这个交叉引用了:
classEffect(object):
def__init__(self):
self.active_ploy=ActivePloy(self)
defactive(self):
"""激活时的处理"""
pass
defdestroy(self):
self.active_ploy.destroy()
classActivePloy(object):
def__init__(self,effect):
self.effect=effect
defactive(self):
"""激活时,激活对应效果"""
self.effect.active()
defdestroy(self):
self.effect=None
于是我们要删除一个效果,就得这样:
char.effect.destroy()
delchar.effect
太麻烦了,不是吗?而且万一一个效果有多个激活策略的话,必须保证Effect把每个ActivePloy的destroy方法都运行一遍,漏了一个都无法保证自身被干净的删除。
我们来分析一下,之所以这么麻烦,就是因为ActivePloy对Effect有一个引用。那么如果ActivePloy不引用Effect不就OK了?这个时候,让我们来试试弱引用。
importweakref
classEffect(object):
def__init__(self):
self.active_ploy=ActivePloy(self)
defactive(self):
"""激活时的处理"""
pass
classActivePloy(object):
def__init__(self,effect):
self.effect=weakref.proxy(effect)#弱引用effect
defactive(self):
"""激活时,激活对应效果"""
self.effect.active()
代码只有一个地方改变了,就是
self.effect=weakref.proxy(effect)
这句的效果就是self.effect可以像往常一样的使用,但是却不会增加effect的引用计数器。换言之,这样写,他们之间的交叉关系消失了!这个时候我们只需要单纯的删掉char.effect,Effect和ActivePloy的实例都会被销毁。
什么,假设ActivePloy在其他地方也被引用了?这样当然只有effect会被销毁。但是我们想让ActivePloy必然随着Effect的销毁而销毁,怎么办呢?那么我们可以改改,给弱引用加上一个回调函数:
classActivePloy(object):
def__init__(self,effect):
self.effect=weakref.proxy(effect,self.on_effect_destroy)#弱引用effect
defactive(self):
"""激活时,激活对应效果"""
self.effect.active()
defon_effect_destroy(self,effect):
"""
effect销毁时会调用这个方法,在这里把对自己的引用都清理干净吧
"""
pass
这样一来,就不用担心删不干净了。