Python Cookbook-6.10 保留对被绑定方法的引用且支持垃圾回收

任务

你想保存一些指向被绑定方法的引用,同时还需要让关联的对象可以被垃圾收集机制处理。

解决方案

弱引用(weakreference)(弱引用在一个对象处于生存期时指向该对象,但如果没有其他正常的引用指向该对象时,这个对象不会被保留)在一些高级编程方法中是一个重要的工具。Python 标准库的 weakref模块允许我们使用弱引用。

然而,weakref的功能无法被直接应用于被绑定方法,除非你采取了一些特殊的预防措施。为了让一个对象在有引用指向其被绑定方法的时候能被垃圾回收机制处理,需要做一些封装工作。将下列代码存为一个名为weakmethod.py 的文件,并放入你的Python的sys.path目录中:

python 复制代码
import weakref,new
class ref(object):
	'''能够封装任何可调用体,特别是被绑定方法,而且被绑
	定方法仍然能被回收处理。与此同时,提供一个普通的弱引用的接口'''
	def __init__(self,fn):
		try:
			#试图获得对象、函数和类
			o,f,c = fn.im_self,fn.im_func,fn,im_class
		except AttributeError:#非被绑定方法
			self._obj = None
			self._func = fn
			self._clas = None
		else:#绑定方法
			if o is None:self._obj = None#...实际上没绑定
			else: self._obj = weakref.ref(o)#...确实绑定了
			self._func = f
			self._clas = c
	def __call__(self):
		if self.obj is None:return self._func
		elif self._obj()is None:return None
		return new.instancemethod(self._func,self.obj(),self._clas)

讨论

一个正常的被绑定方法拥有一个指向此方法所属对象的强引用。这意味着除非这个被绑定方法被消灭掉,否则该对象不能被当做垃圾重新回收。

python 复制代码
>>> class C(object):
	def f(self):
		print "Hello"
	def __del__(self):
		print "C dying"
>>> c = C()
>>> cf = c.f
>>> del c#c眨巴眨巴眼睛活得好好的...
>>> del cf#...直到我们干掉了被绑定的方法,它才终于安心地去了
C dying

很多时候这种行为方式很方便,但有时它却达不到你想要的效果。比如,你想实现一个事件分发的系统,你可能不希望由于一个事件处理者(比如一个被绑定函数)的存在,整个关联对象都无法被回收。因而一个很自然的想法是使用弱引用。不过,一个普通的指向被绑定方法的 weakref.ref并不会按照我们的期望工作,那是因为被绑定方法是一级对象(frst-class objects)。指向被绑定方法的弱引用的保质期总是很短的,在解除引用时它们总是返回None,除非还有另外一个强引用指向了这个被绑定方法。

举个例子,下面的代码基于 Python 标准库 weakref模块,并不会打印"hello",却会抛出一个异常:

python 复制代码
>>> import weakref
>>> c = C()
>>> cf = weakref.ref(c.f)
>>> cf#先瞧瞧这是什么东西...
<weakref at 80ce394; dead>
>>> cf()()
Traceback(most recent call last):
File"", line 1, in ?
TypeError: object of type 'None' is not callable

另一方面,下面展示的在 weakmethod模块中的ref类则允许你使用指向被绑定方法的弱引用:

python 复制代码
>>> import weakmethod
>>> cf = weakmethod.ref(c.f)
>>> cf()()# 哇哦!它是活的!
Hello
>>> del c#...然后就死掉了
C dying
>>> print cf()
None

调用 weakmethod.ref实例,即指向一个被绑定方法的引用,与调用 weakref.ref 指向一个函数对象的实例具有相同的语义:如果引用无效,它返回None;否则就返回引用。事实上,此例中它返回了一个新建的new.instancemethod(它持有一个指向对象的强引用,因此,应当确保不持有这个引用,除非你想让那个对象多存活一段时间)。

注意,本节的解决方案代码的设计很讲究,可以很容易地把任何你想封装的可调用体封入一个 ref实例中,无论是方法(绑定或未绑定的)、函数或其他任何东西。但只有当你试图封装一个被绑定的方法时,它才会有弱引用的语义,对其他的情况,ref就像一个普通(强)引用,一直指向处于生存期的可调用体。这种方式让可以用ref封装任意的可调用体,而无须为任何特殊情况做特殊处理。

如果需要的语义接近于 weakrefproxy 的语义,那就更容易实现了,例如从本节的 ref类派生一个子类。当你调用 proxy时,proxy会用相同的参数调用引用。如果被引用的对象已经消亡,会生成weakref.ReferenceError异常。下面是 proxy 类的一个实现:

python 复制代码
class proxy(ref):
	def __call__(self,*args,**kwargs):
		func = ref.__call__(self)
		if func is None:
			raise weakref,ReferenceError('referent object is dead')
		else:
			return func(*args,**kwargs)
	def __eq__(self,other):
		if type(other) != type(self):
			return False
		return ref.__call__(self) == ref.__call__(other)
相关推荐
微小冷3 分钟前
Vimba相机二次开发教程,基于Python
开发语言·python·二次开发·相机开发·vimba相机·vimba
从0至115 分钟前
C++编程入门:从基础到复合类型
开发语言·c++
java1234_小锋16 分钟前
【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 热词数量分析日期统计功能实现
python·自然语言处理·flask
山烛32 分钟前
KNN 算法中的各种距离:从原理到应用
人工智能·python·算法·机器学习·knn·k近邻算法·距离公式
guozhetao1 小时前
【ST表、倍增】P7167 [eJOI 2020] Fountain (Day1)
java·c++·python·算法·leetcode·深度优先·图论
墨染点香1 小时前
第七章 Pytorch构建模型详解【构建CIFAR10模型结构】
人工智能·pytorch·python
枫叶丹41 小时前
【Qt开发】信号与槽(二)-> 信号和槽的使用
开发语言·qt
阿什么名字不会重复呢1 小时前
在线工具+网页平台来学习和操作Python与Excel相关技能
python·数据分析
Vertira2 小时前
python 阿里云 安装 dashscope的简介、安装
开发语言·python
gc_22993 小时前
学习Python中Selenium模块的基本用法(1:简介)
python·selenium