[Python标准库]weakref——对象的非永久引用
2015-12-27 23:20
585 查看
[Python标准库]weakref——对象的非永久引用
作用:引用一个“昂贵”的对象,不过如果不再有其他非弱引用,则允许垃圾回收器回收其内存。
Python 版本:2.1 及以后版本
weakref 模块支持对象的弱引用。正常的引用会增加对象的引用计数,避免它被垃圾回收。但并不总希望如此,比如有时可能会出现一个循环引用,或者有时可能要构建一个对象缓存,需要内存时则要删除这个缓存。弱引用(weak reference)是避免对象被自动清除的一个对象句柄。
引用
对象的弱引用通过 ref 类管理。要获取原对象,可以调用引用对象。
在这里,由于 obj 在第二次调用引用之前已经删除,所以 ref 返回 None。
引用回调
ref 构造函数接受一个可选的回调函数,删除所引用的对象是会调用这个函数。
引用已经“死亡”不再引用原对象时,这个回调会接受引用对象作为参数。这个特性的一种用法就是从缓存删除弱引用对象。
代理
有时使用代理比使用弱引用更为方便。使用代理时可以像使用原对象一样,而且不要求访问对象之前先调用代理。这说明,可以将代理传递到一个库,而这个库并不知道它接收的是一个引用而不是真正的对象。
如果引用对象删除后再访问代理,会产生一个 ReferenceError 异常。
循环引用
弱引用有一种用法,即在不阻止垃圾回收时允许循环引用。下面的例子展示了图中包含一个循环时使用常规对象和使用代理的区别。
weakref_graph.py 中的 Graph 类接受给定的任意对象作为序列中的“下一个”节点。为简洁起见,这个实现支持从各节点有一个“传出”(outgoing)引用,通常这样做用途很有限,不过可以很容易为这些例子创建循环。函数 demo() 是一个工具函数,通过创建一个循环然后删除各个引用来尝试使用 Graph 类。
这个例子使用 gc 模块帮助调试内存泄露。DEBUG_LEAK 标志会使 gc 打印那些无法看到的对象的有关信息,而不是通过垃圾回收器中这些对象的引用。
即使在 demo() 中删除了 Graph 实例的本地引用,图仍然显示在垃圾列表中,不能回收。而且垃圾列表中还会找到多个字典。它们是来自 Graph 实例的 __dict__ 值,包含了这些对象的属性。可以强制删除这些图,因为程序知道它们是什么。向解释器传入 -u 选项,启用非缓冲 I/O,从而确保这个示例程序中 print 语句的输出(写至标准输出)和 gc 的调试输出(写至标准错误输出)正确的隔行显示。
下一步是创建一个更智能的 WeakGraph 类,它知道如何在检测到一个循环时使用弱引用来避免建立常规引用循环。
由于 WeakGraph 实例使用代理来指示已看到的对象,随着 demo() 删除了对象的所有本地引用,循环会断开,这样垃圾回收器就可以将这些对象删除。
缓存对象
ref 和 proxy 类可以认为是“底层”实现。尽管它们对于维护单个对象的弱引用很有用。并允许将循环垃圾回收,但 WeakKeyDictionary 和 WeakValueDictionary 提供了一个更合适的 API 来创建多个对象的缓存。
WeakValueDictionary 使用其中保存的值的弱引用,当其他代码不再实际使用这些值时允许将其垃圾回收。通过使用垃圾回收器的显示调用,由此说明了使用常规字典和 WeakValueDictionary 完成内存处理的差别。
如果循环变量指示缓存的值,这些循环变量必须显示清除,从而使对象的引用计数减少。否则,垃圾回收器不会删除这些对象,它们仍会保留在缓存中。类似地,all_refs 变量用来维护引用,避免它们过早地被垃圾回收。
WeakKeyDictionary 的工作原理与此类似,不过它使用字典中键的弱引用,而不是字典中的值。
作用:引用一个“昂贵”的对象,不过如果不再有其他非弱引用,则允许垃圾回收器回收其内存。
Python 版本:2.1 及以后版本
weakref 模块支持对象的弱引用。正常的引用会增加对象的引用计数,避免它被垃圾回收。但并不总希望如此,比如有时可能会出现一个循环引用,或者有时可能要构建一个对象缓存,需要内存时则要删除这个缓存。弱引用(weak reference)是避免对象被自动清除的一个对象句柄。
引用
对象的弱引用通过 ref 类管理。要获取原对象,可以调用引用对象。
import weakref class ExpensiveObject(object): def __del__(self): print '(Deleting %s)' % self obj = ExpensiveObject() r = weakref.ref(obj) print 'obj:', obj print 'ref:', r print 'r():', r() print 'deleting obj' del obj print 'r():', r()
在这里,由于 obj 在第二次调用引用之前已经删除,所以 ref 返回 None。
引用回调
ref 构造函数接受一个可选的回调函数,删除所引用的对象是会调用这个函数。
import weakref class ExpensiveObject(object): def __del__(self): print '(Deleting %s)' % self def callback(reference): """Invoked when referenced object is deleted""" print 'callback(', reference, ')' obj = ExpensiveObject() r = weakref.ref(obj, callback) print 'obj:', obj print 'ref:', r print 'r():', r() print 'deleting obj' del obj print 'r():', r()
引用已经“死亡”不再引用原对象时,这个回调会接受引用对象作为参数。这个特性的一种用法就是从缓存删除弱引用对象。
代理
有时使用代理比使用弱引用更为方便。使用代理时可以像使用原对象一样,而且不要求访问对象之前先调用代理。这说明,可以将代理传递到一个库,而这个库并不知道它接收的是一个引用而不是真正的对象。
import weakref class ExpensiveObject(object): def __init__(self, name): self.name = name def __del__(self): print '(Deleting %s)' % self obj = ExpensiveObject('My Object') r = weakref.ref(obj) p = weakref.proxy(obj) print 'via obj:', obj.name print 'via ref:', r().name print 'via proxy:', p.name del obj print 'via proxy:', p.name
如果引用对象删除后再访问代理,会产生一个 ReferenceError 异常。
循环引用
弱引用有一种用法,即在不阻止垃圾回收时允许循环引用。下面的例子展示了图中包含一个循环时使用常规对象和使用代理的区别。
weakref_graph.py 中的 Graph 类接受给定的任意对象作为序列中的“下一个”节点。为简洁起见,这个实现支持从各节点有一个“传出”(outgoing)引用,通常这样做用途很有限,不过可以很容易为这些例子创建循环。函数 demo() 是一个工具函数,通过创建一个循环然后删除各个引用来尝试使用 Graph 类。
import gc from pprint import pprint import weakref 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): "Generate the nodes in the graph sequence." 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 self.set_next(None) def collect_and_show_garbage(): "Show what garbage is present." 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 'Removing last reference:' one = None collect_and_show_garbage()
这个例子使用 gc 模块帮助调试内存泄露。DEBUG_LEAK 标志会使 gc 打印那些无法看到的对象的有关信息,而不是通过垃圾回收器中这些对象的引用。
import gc from pprint import pprint import weakref from weakref_graph import Graph, demo, 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()
即使在 demo() 中删除了 Graph 实例的本地引用,图仍然显示在垃圾列表中,不能回收。而且垃圾列表中还会找到多个字典。它们是来自 Graph 实例的 __dict__ 值,包含了这些对象的属性。可以强制删除这些图,因为程序知道它们是什么。向解释器传入 -u 选项,启用非缓冲 I/O,从而确保这个示例程序中 print 语句的输出(写至标准输出)和 gc 的调试输出(写至标准错误输出)正确的隔行显示。
下一步是创建一个更智能的 WeakGraph 类,它知道如何在检测到一个循环时使用弱引用来避免建立常规引用循环。
import gc from pprint import pprint import weakref from weakref_graph import Graph, demo, collect_and_show_garbage class WeakGraph(Graph): def set_next(self, other): if other is not None: # See if we should replace the reference # to other with a weakref. if self in other.all_nodes(): other = weakref.proxy(other) super(WeakGraph, self).set_next(other) return demo (WeakGraph)
由于 WeakGraph 实例使用代理来指示已看到的对象,随着 demo() 删除了对象的所有本地引用,循环会断开,这样垃圾回收器就可以将这些对象删除。
缓存对象
ref 和 proxy 类可以认为是“底层”实现。尽管它们对于维护单个对象的弱引用很有用。并允许将循环垃圾回收,但 WeakKeyDictionary 和 WeakValueDictionary 提供了一个更合适的 API 来创建多个对象的缓存。
WeakValueDictionary 使用其中保存的值的弱引用,当其他代码不再实际使用这些值时允许将其垃圾回收。通过使用垃圾回收器的显示调用,由此说明了使用常规字典和 WeakValueDictionary 完成内存处理的差别。
import gc from pprint import pprint import weakref gc.set_debug(gc.DEBUG_LEAK) class ExpensiveObject(object): def __init__(self, name): self.name = name def __repr__(self): return 'ExpensiveObject(%s)' % self.name def __del__(self): print ' (Deleting %s)' % self def demo(cache_factory): # hold objects so any weak references # are not removed immediately all_refs = {} # create the cache using the factory print 'CACHE TYPE:', cache_factory cache = cache_factory() for name in [ 'one', 'two', 'three' ]: o = ExpensiveObject(name) cache[name] = o all_refs[name] = o del o # decref print ' all_refs =', pprint(all_refs) print '\n Before, cache contains:', cache.keys() for name, value in cache.items(): print ' %s = %s' % (name, value) del value # decref # Remove all references to the objects except the cache print '\n Cleanup:' del all_refs gc.collect() print '\n 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)
如果循环变量指示缓存的值,这些循环变量必须显示清除,从而使对象的引用计数减少。否则,垃圾回收器不会删除这些对象,它们仍会保留在缓存中。类似地,all_refs 变量用来维护引用,避免它们过早地被垃圾回收。
WeakKeyDictionary 的工作原理与此类似,不过它使用字典中键的弱引用,而不是字典中的值。
相关文章推荐
- Python字典对象实现原理
- 用python写一个进程
- python join 和 split的常用使用方法
- python中的列表1
- python socket编程(2)-SocketServer 模块
- LightMysql:为方便操作MySQL而封装的Python类
- 用python从excel表格中读取数据生成可以放在科技论文中的图片
- python socket编程(1)
- Python学习笔记1-搭建Python环境 和 Python Hello World!
- Python学习笔记1-搭建Python环境 和 Python Hello World!
- python import模块方法
- 我的Python成长之路---第一天---Python基础(2)---2015年12月26日(雾霾)
- Python学习——socketservice例子的一个问题
- 关于Python2的一点想法以及遇到的问题
- windows 环境下 python 安装matplotlib
- python学习之字符串(上)
- Python 基础学习(一)
- python如何处理解析word文档doc docx , python-docx,python-docx2txt,zipfile
- python numpy教程
- 栅栏加解密python实现(支持密钥加密)