文章目录

  • 创建弱引用
  • 创建代理对象
  • 循环引用
  • 缓存对象 (WeakValueDictionary)

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

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

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

弱引用的存在价值 在对象群组内部使用弱引用(即不会在引用计数中被计数的引用)有时能避免出现引用环,因此弱引用可用于解决循环引用的问题。
  在计算机程序设计中,弱引用,与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则可能在任何时刻被回收。弱引用的主要作用就是减少循环引用,减少内存中不必要的对象存在的数量。

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

创建弱引用

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

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

>>> import sys
>>> import weakref
>>> class Man():
...     def __init__(self, name):
...             self.name = name
...
>>> man0 = Man('zhe')    # 增加一个引用  count = 1
>>> sys.getrefcount(man0)
2
>>> r = weakref.ref(man0)   # 增加一个弱引用  count = 1
>>> sys.getrefcount(man0)
2
>>> r   # 获取弱引用所指向的对象
<weakref at 0x0000026AF3974688; to 'Man' at 0x0000026AF398C710>
>>> man1 = r()
>>> man0 is man1
True
>>> sys.getrefcount(man0)
3
>>> man0 = None
>>> man1 = None
>>> r   # 当对象引用计数为零时,弱引用失效。
<weakref at 0x0000026AF3974688; dead>

  1. 上面的代码中,我们使用 sys 包中的 getrefcount() 来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给 getrefcount() 时,参数实际上创建了一个临时的引用。因此, getrefcount() 所得到的结果,会比期望的多 1 。
  2. 一旦没有了对这个对象的其它的引用,调用弱引用将返回None,因为Python已经销毁了这个对象。 注意:大部分的对象不能通过弱引用来访问。
  3. weakref 模块中的 getweakrefcount(obj) 和 getweakrefs(obj) 分别返回弱引用数和关于所给对象的引用列表。
  4. 弱引用对于创建对象(这些对象很费资源)的缓存是有用的。
  5. 对弱引用的使用来访问原对象要通过 weakref() 的形式。 其实可以创建代理对象对原对象进行访问。

创建代理对象

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

>>> import sys
>>> import weakref
>>> class Man():
...     def __init__(self, name):
...             self.name = name
...
>>> def callback_ref(self):
...     print (self)
...     print ("callback_ref")
...
>>> def callback_proxy(self):
...     print (self)
...     print ("callback_proxy")
...
>>> man = Man('zhe')   # 引用计数 +1
>>> sys.getrefcount(man)
2
>>> man_ref = weakref.ref(man, callback_ref)  # 弱引用 引用计数不变
>>> sys.getrefcount(man)
2
>>> man_ref   # 弱引用对象
<weakref at 0x0000019A63664638; to 'Man' at 0x0000019A6367C668>
>>> man_ref.name   # 对原对象的访问形式错误
Traceback (most recent call last):File "<stdin>", line 1, in <module>
AttributeError: 'weakref' object has no attribute 'name'
>>> man_ref().name    # 正确的对原对象的访问形式
'zhe'
>>> man_proxy = weakref.proxy(man, callback_proxy)   # 使用代理 引用计数不变
>>> sys.getrefcount(man)
2
>>> man_proxy  # 代理对象
<weakproxy at 0x0000019A634D6BD8 to Man at 0x0000019A6367C668>
>>> man_proxy.name   # 访问形式与原引用相同
'zhe'
>>> del man   # 注
Exception ignored in: <function callback_proxy at 0x0000019A636807B8>
Traceback (most recent call last):File "<stdin>", line 2, in callback_proxy
ReferenceError: weakly-referenced object no longer exists
<weakref at 0x0000019A63664638; dead>
callback_ref

  1. 删除原对象,再对其代理的访问,会引起一个 weakref.ReferenceError 错误。
  2. 删除原对象,再对其弱引用的访问,<weakref at 0x0000019A63664638; dead>。

循环引用

前面说过,使用弱引用,可以解决循环引用不能被垃圾回收的问题。
首先我们看下常规的循环引用,先创建一个简单的Graph类,然后创建三个Graph实例:

import gc
from pprint import pprint
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'class Graph(object):def __init__(self, name):self.name = nameself.other = Nonedef set_next(self, other):print ("%s.set_next(%r)" % (self.name, other))self.other = otherdef all_nodes(self):yield selfn = self.otherwhile n and n.name !=self.name:yield nn = n.otherif n is self:yield nreturndef __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 = Nonetwo = Noneprint ("After 2 references removed")print (str(one))collect_and_show_garbage()print()print ("removeing last reference")one = Nonecollect_and_show_garbage()print()gc.set_debug(gc.DEBUG_LEAK)
print ("Setting up the cycle")
print ()
demo(Graph)gc.garbage[0].set_next(None)
while gc.garbage:del gc.garbage[0]
print (collect_and_show_garbage())

结果

Setting up the cycleSet up graph:
one.set_next(<Graph at 0x14496cdecc0 name=two>)
two.set_next(<Graph at 0x14496cdeeb8 name=three>)
three.set_next(<Graph at 0x14496cdee48 name=one>)Graph:
one->two->three->one
Collecting...
unreachable objects: 0
garbage:
[]After 2 references removed
one->two->three->one
Collecting...
unreachable objects: 0
garbage:
[]removeing last reference
Collecting...
(Deleting one)
(Deleting two)
(Deleting three)
unreachable objects: 6
garbage:
[<Graph at 0x14496cdee48 name=one>,{'name': 'one', 'other': <Graph at 0x14496cdecc0 name=two>},<Graph at 0x14496cdecc0 name=two>,{'name': 'two', 'other': <Graph at 0x14496cdeeb8 name=three>},<Graph at 0x14496cdeeb8 name=three>,{'name': 'three', 'other': <Graph at 0x14496cdee48 name=one>}]
one.set_next(None)Collecting...
unreachable objects: 0
garbage:
[]
None

解释

  1. 从结果中我们可以看出,即使我们删除了Graph实例的本地引用,它依然存在垃圾列表中,不能回收。
  2. 接下来创建使弱引用的WeakGraph类,防止循环引用。
import weakref
import gc
from pprint import pprintclass Graph(object):def __init__(self, name):self.name = nameself.other = Nonedef set_next(self, other):print ("%s.set_next(%r)" % (self.name, other))self.other = otherdef all_nodes(self):yield selfn = self.otherwhile n and n.name !=self.name:yield nn = n.otherif n is self:yield nreturndef __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)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)returndef collect_and_show_garbage():print ("Collecting...")n = gc.collect()print ("unreachable objects:", n)print ("garbage:",)pprint(gc.garbage)print ()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 = Nonetwo = Noneprint ("After 2 references removed")print (str(one))collect_and_show_garbage()print()print ("removeing last reference")one = Nonecollect_and_show_garbage()demo(WeakGraph)

结果

Set up graph:
one.set_next(<WeakGraph at 0x29efa09ecc0 name=two>)
two.set_next(<WeakGraph at 0x29efa09ef28 name=three>)
three.set_next(<weakproxy at 0x0000029EFA0D6458 to WeakGraph at 0x0000029EFA09EEB8>)Graph:
one->two->three
Collecting...
unreachable objects: 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:
[]

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

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

缓存对象 (WeakValueDictionary)

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

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

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

import weakref
import gc
from pprint import pprint# gc.set_debug(gc.DEBUG_LEAK)class Man(object):def __init__(self, name):self.name = namedef __repr__(self):return '<Man name=%s>' % self.namedef __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] = manall_refs[name] = mandel manprint ("all_refs=",)pprint(all_refs)print()print ("before, cache contains:", cache.keys())for name, value in cache.items():print ("%s = %s" % (name, value))print ("\ncleanup")del all_refsgc.collect()print()print ("after, cache contains:", cache.keys())for name, value in cache.items():print ("%s = %s" % (name, value))print ("demo returning")returndemo(dict)
print ()
print("====The end of dict process and the begin of weakref.WeakValueDictionary process:====")
demo(weakref.WeakValueDictionary)

结果

cache type: <class 'dict'>
all_refs=
{'Green': <Man name=Green>, 'Jim': <Man name=Jim>, 'Tom': <Man name=Tom>}before, cache contains: dict_keys(['Jim', 'Tom', 'Green'])
Jim = <Man name=Jim>
Tom = <Man name=Tom>
Green = <Man name=Green>cleanupafter, cache contains: dict_keys(['Jim', 'Tom', 'Green'])
Jim = <Man name=Jim>
Tom = <Man name=Tom>
Green = <Man name=Green>
demo returning
deleting <Man name=Jim>
deleting <Man name=Tom>
deleting <Man name=Green>====The end of dict process and the begin of weakref.WeakValueDictionary process:====
cache type: <class 'weakref.WeakValueDictionary'>
all_refs=
{'Green': <Man name=Green>, 'Jim': <Man name=Jim>, 'Tom': <Man name=Tom>}before, cache contains: <generator object WeakValueDictionary.keys at 0x0000026245B74930>
Jim = <Man name=Jim>
Tom = <Man name=Tom>
Green = <Man name=Green>cleanup
deleting <Man name=Jim>
deleting <Man name=Tom>after, cache contains: <generator object WeakValueDictionary.keys at 0x0000026245B74930>
Green = <Man name=Green>
demo returning
deleting <Man name=Green>

  1. 采用 dict 可以看出 当删除所有 all_refs 引用时,dict 中依然存有强引用。
  2. 而采用 WeakValueDictionary 时,当删除所有 all_refs 引用时,WeakValueDictionary container 里为空,即就是 WeakValueDictionary 存的是弱引用。

python 弱引用相关推荐

  1. 弱引用的用途:在底层C++对象被上层python脚本对象使用时(转)

    在使用python脚本和底层C++对象进行交互的过程中发生了一个问题:由于底层C++对象的创建和删除决定权由底层决定,当底层决定删除这些对象而上层仍然在"强引用"这些对象的时候,就 ...

  2. Python的弱引用

    python的弱引用指引用一个对象但不增加它的引用计数器.这么做的好处是什么呢?什么时候需要考虑用若引用呢? 假设我们在设计一个游戏,有一个角色类Char,我们要给他添加一个效果(比如中毒),于是设计 ...

  3. python字典弱引用_python 自身遍历及弱引用的简单示例

    对python这个高级语言感兴趣的小伙伴,下面一起跟随编程之家 jb51.cc的小编两巴掌来看看吧! 在[python 标准库]中看到的一段代码,非常有帮助: # @param python 图 自身 ...

  4. python对象回收_python 引用,拷贝,对象回收,弱引用

    引用 python中,在对对象赋值,参数传递,函数返回等等, 都是引用传递的. 直接copy个例子来[1]: a = [1, 2, 3] b = a b.append(5) print a, b 输出 ...

  5. Python 中弱引用的神奇用法与原理探析

    文章源地址:神奇的弱引用 | 小菜学编程 背景 开始讨论弱引用( weakref )之前,我们先来看看什么是弱引用?它到底有什么作用? 假设我们有一个多线程程序,并发处理应用数据: # 占用大量资源, ...

  6. python雪峰_python雪峰python 图 自身遍历及弱引用使用

    在[python 标准库]中看到的一段代码,非常有帮助:def all_nodes(self): yield self n = self.other while n and n.name != sel ...

  7. 【python】对象释放,弱引用weaker.ref

    class A:def __del__(self):print("in__del__")>>> a = A() >>> a = None in_ ...

  8. sqlserver2008未将对象引用设置到对象的实例_面试官:ThreadLocal 的内存泄漏是弱引用导致的,你确定?...

    面试官:ThreadLocal 了解吗? Python 小星:线程局部变量,多线程下能保证各个线程的变量相对独立于其他线程的变量. 面试官:那你说下它是如何保证线程隔离的? Python 小星:每个线 ...

  9. Java 7之基础 - 强引用、弱引用、软引用、虚引用

    2019独角兽企业重金招聘Python工程师标准>>> 1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.如下 ...

最新文章

  1. 1. 文件系统——磁盘分区、各目录功能、硬盘
  2. Centos7 安装mongodb记录
  3. duilib自定义消息
  4. jQUery中closest和parents的主要区别是
  5. 2013-11-11 Oracle 课堂测试 练习题 例:BULK COLLECT及return table
  6. Ubuntu作为服务器其tomcat被远程访问问题
  7. NLP之路-python爬虫
  8. mount: none already mounted or /cgroup busy
  9. 扫描仪twain驱动是什么_扫描仪无法显示怎么办 扫描仪无法显示解决方法【详解】...
  10. unity 电梯_unity应用实例——电梯模拟系统
  11. 在字符串中查找id值MySQL
  12. apk系统签名和发布软件签名的理解
  13. MAC之U盘(制作U盘启动必须是在mac系统中)
  14. 在局域网内怎样使两台计算机共享,实现局域网内两台windows计算机之间数据共享...
  15. 在多个QQ号码间共享自定义表情(转)
  16. FreeRTOS学习笔记(11)——CPU使用率统计
  17. 嵌入式--接口--MIPI接口
  18. 加拿大签证办理时解释信Explanation of Letter参考
  19. java hypot_Java StrictMath hypot()方法与示例
  20. linux网络设备和一些知识点总结

热门文章

  1. c语言五猴分桃案例分析,C语言程序设计五猴分桃问题实验报告.doc
  2. 2021-11-08FGUI 使用
  3. 技法(但请少用):为非活动窗口绘制活动的标题栏
  4. 利用incapsula缓解ddos攻击
  5. 计算机usb无法读取u盘启动,U盘无法识别的USB设备怎么办解决
  6. ArcEngine旋转IRotateTracker
  7. Linux磁盘相关-分区与修复
  8. webstorm 下载安装及破解
  9. vue脚手架安装时出现Error EPERM operation not permitted这个错误
  10. 调用Google翻译 语音接口