介绍

weakref支持对象的弱引用,正常的引用会增加对象的引用计数,并避免它被垃圾回收。但结果并不是总和期望的那样,比如有时候可能会出现一个循环引用,或者有时候需要内存时可能要删除对象的缓存。而弱引用(weak reference)是一个不会增加引用计数的对象句柄

引用

import weakref

'''
对象的弱引用要通过ref类来管理。要获取原对象,可以调用引用对象
''' class RefObject:
def __del__(self):
print("del executed") obj = RefObject()
# 创建弱引用
r = weakref.ref(obj) print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964470>
# 显示关联RefObject
print("ref:", r) # ref: <weakref at 0x000000000051BA48; to 'RefObject' at 0x0000000002964470> # 引用r加上(),等价于obj,因此得到RefObject的实例对象
print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964470> # 删除obj执行析构函数
del obj # del executed
# 之前说过调用r()等价于调用obj,但是obj被删除了,所以返回None
# 从这里返回None也能看出这个弱引用是不会增加引用计数的
print("r():", r()) # r(): None

引用回调

import weakref

'''
ref构造函数可以接受一个可选的回调函数,删除引用所指向的对象时就会调用这个回调函数
''' class RefObject:
def __del__(self):
print("del executed") def callback(reference):
print(f"callback : {reference}") obj = RefObject()
r = weakref.ref(obj, callback)
'''
当引用所引用的原对象"死亡"时,这个回调会接受这个引用对象作为参数。
这种特性的一种用法就是从缓存中删除弱引用对象。
''' print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964630>
print("ref:", r) # ref: <weakref at 0x0000000001D2BA48; to 'RefObject' at 0x0000000002964630>
print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964630> del obj # 删除引用指向的对象
"""
del executed
callback : <weakref at 0x0000000001D2BA48; dead> 删除obj,执行回调,显示dead
"""
print("r():", r()) # r(): None

最终化对象

import weakref

'''
清理弱引用时要对资源完成更健壮的管理,可以使用finalize将回调与对象关联。
finalize实例会一直保留(直到所关联的对象被删除),即使没有保留最终化对象的引用
''' class RefObj:
def __del__(self):
print("xxx") def on_finalize(*args):
print(f"on_finalize: {args}") obj = RefObj()
weakref.finalize(obj, on_finalize, "callback的参数") del obj
'''
xxx
on_finalize: ('callback的参数',)
'''
# finalize的参数包括要跟踪的对象,对象被垃圾回收时要调用的callback,以及参数(可以是位置参数,也可以是关键字参数) # finalize实例对象还有一个atexit属性,用来控制程序退出时是否调用这个回调(如果还未调用)
obj1 = RefObj()
f = weakref.finalize(obj1, on_finalize, "callback的参数")
# 默认是调用回调,但是将atexit设置为False会禁用这种行为
f.atexit = False
'''
不会有任何的输出,注意:这里我虽然没有显示的删除obj1,但也能够说明结论
因为在f.atexit=True的情况下,即使不删除也依旧会执行callback。
原因是即使你不手动删除,但是对象已经被创建出来了,而程序结束的那一刻,也会执行析构函数的。因为对象总是要回收的,即使你不调用del,那么程序执行完毕的时候也会自动调用。
所以默认f.atexit = True是会打印的,但是现在没有打印,所以确实被禁用了
'''
import weakref

'''
如果向finalize实例提供一个跟踪对象的引用,这便会导致一个引用被保留,所以这个对象永远不会被垃圾回收
''' class RefObj:
def __del__(self):
print("xxx") def on_finalize(*args):
print(f"on_finalize: {args}") obj = RefObj()
obj_id = id(obj)
# 这里我将obj实例作为参数传进去了,这样的后果就是obj不会被回收,即使你删除了
f = weakref.finalize(obj, on_finalize, obj)
f.atexit = False # 删除obj,让obj不再指向之前的对象
del obj
import gc
# 获取所有的对象
for o in gc.get_objects():
if id(o) == obj_id:
# 结果发现真的没有被回收,因为引用不止obj一个,还有其它人在用
print("found uncollected object in gc") # found uncollected object in gc

代理

import weakref

'''
有时候使用代理比使用弱引用更方便。使用代理可以像使用原对象一样,而且不要求在访问对象之前先调用代理。
这说明,可以将代理传递到一个库,而这个库并不知道它接收的是一个代理而不是真正的一个对象。
''' class RefObj: def __init__(self, name):
self.name = name def __del__(self):
print("xxx") obj = RefObj("my obj")
r = weakref.ref(obj)
p = weakref.proxy(obj) # 可以看到引用加上()才相当于原来的对象
# 而代理不需要,直接和原来的对象保持一致
print("via obj:", obj.name) # via obj: my obj
print("via ref:", r().name) # via ref: my obj
print("via proxy:", p.name) # via proxy: my obj del obj # xxx
try:
# 删除对象之后,再调用引用,打印为None
print(r()) # None
# 但是如果调用代理的话,则会抛出一个ReferenceError
print(p)
except Exception as e:
print(e) # weakly-referenced object no longer exists

自定义类指定弱引用

当我们自定义一个类的时候,如果为了省内存,那么会不使用__dict__属性,因为每一个类或者实例都会有一个自己的属性字典__dict__,而我们知道字典使用的是哈希表,这是一个使用空间换时间的数据结构,因此如果想省内存的话,那么我们通常的做法是指定__slots__属性,这样就不会再有__dict__属性了。

class A:
__slots__ = ('name', 'age') def __init__(self, name, age):
# 此时在__init__里面,只能有self.name和self.age
# 这是因为我们在__slots__里面只指定了name和age
# 因此当我们需要省内存、并且属性固定的时候,可以指定__slots__属性
self.name = name
self.age = age def __str__(self):
return f"name is {self.name}, age is {self.age}" if __name__ == '__main__':
import weakref
a = A("hanser", 27)
try:
r = weakref.proxy(a)
except TypeError as e:
print(e) # cannot create weak reference to 'A' object

但是我们发现此时这个A的实例对象是没有办法被弱引用的,因为我们指定了__slots__,那么要怎么做呢?直接在__slots__里面加上一个属性就好了。

class A:
# 多指定一个__weakref__,表示支持弱引用
__slots__ = ('name', 'age', '__weakref__') def __init__(self, name, age):
self.name = name
self.age = age def __str__(self):
return f"name is {self.name}, age is {self.age}" if __name__ == '__main__':
import weakref
a = A("hanser", 27)
r = weakref.proxy(a)
print(r)

可以看到此时就支持弱引用了。

weakref:对象的弱引用的更多相关文章

  1. C#中考虑为大对象使用弱引用

    1.无论怎样尽力,我们总是会使用到某些需要大量内存的数据,而这些内存并不需要经常访问.或许你需要从一个大文件中查找某个特定的值,或者算法需要一个较大的查询表.这时,你也许会采用2中不太好做法:第一种是 ...

  2. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象

    减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...

  3. Python3标准库:weakref对象的非永久引用

    1. weakref对象的非永久引用 weakref模块支持对象的弱引用.正常的引用会增加对象的引用数,并避免它被垃圾回收.但结果并不总是如期望中的那样,比如有时可能会出现一个循环引用,或者有时需要内 ...

  4. java对象的四种引用:强引用、软引用、弱引用和虚引用

    在JDK1.2之前,创建的对象只有在处于可触及(reachable)的状态下,才能被程序使用.也就是说,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.垃圾回收器一旦发现这些无用对象,就会对 ...

  5. java对象的强引用,软引用,弱引用和虚引用

    1.强引用 以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用.如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它.当内存空 间不足,Java虚拟机宁愿抛出Out ...

  6. 谈谈Java对象的强引用,软引用,弱引用,虚引用分别是什么

    整体结构 java提供了4中引用类型,在垃圾回收的时候,都有自己的各自特点. 为什么要区分这么多引用呢,其实这和Java的GC有密切关系. 强引用(默认支持模式) 把一个对象赋给一个引用变量,这个引用 ...

  7. 合理使用软引用和弱引用,提升JVM内存使用性能

    在项目运行时,OOM异常是比较处理的,因为从日志看出的发生异常的代码点可能仅仅是最后一根稻草,从中可能未必能发现OOM的原因,而且OOM未必是固定重现的. 上医治未病,与其等OOM问题发生时再通过看日 ...

  8. 通过软引用和弱引用提升JVM内存使用性能的方法(面试时找机会说出,一定能提升成功率)

    初学者或初级程序员在面试时如果能证明自己具有分析内存用量和内存调优的能力,这相当有利,因为这是针对5年左右相关经验的高级程序员的要求.而对于高级程序员来说,如果能在面试时让面试官感觉你确实做过内存调优 ...

  9. 你不可不知的Java引用类型之——弱引用

    定义 弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型.在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收. 说明 弱 ...

随机推荐

  1. 手把手教你如何玩转Solr(包含项目实战)

    一:Solr简介       Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引 ...

  2. MATLAB学习(十)实现文件、图像读写

    t=1:5; s1=sin(t); s2=cos(t); s=[t;s1;s2]; fid1=fopen('test.dat','wt'); fprintf(fid1,'\nThis is a For ...

  3. 怎样做delphi程序连接数据库失败,单机确定就关闭窗体

    我单机确定还会显示窗体?? Application.Terminate;

  4. 树莓派实现摄像头监控(使用motion和mjpg-streamer)

    购买raspBerryCarmen,大概20元, 启动树莓派,安装: `sudo apt install motion` 配置/etc/motion/motion.conf, `sudo vim /e ...

  5. Golang gRPC微服务02: helloworld

    安装protobuf 在windows下,直接下载release版本https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.0然后把 ...

  6. python-linux-集群nginx

    一命令基本格式 ----cd ~ 家 ----cd / 根 ---cd ..  上级 ----pwd  当前路径 ----ls -l   -a  -h ----权限rwxrwxrwx  755 二文件 ...

  7. 【计算机视觉】极限优化:Haar特征的另一种的快速计算方法—boxfilter

    这种以Boxfilter替代integral image 的方法很难使用到haar.LBP等特征检测中,因为像下面说的,它不支持多尺度,也就是说所提取的特征必须是同一个大小,最起码同一个宽高比的,这一 ...

  8. 【ARM-Linux开发】Linux的SOCKET编程详解

    Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系 ...

  9. layui select 联动渲染赋值不了数据的问题

    今天用 layui做select的时候,数据老是看不到,而且联动的数据是对不上的,看了网上一堆是 最后要用 form.render('select'); 这个是必须的, 但是我用了还是这样,其实是la ...

  10. Linux 网络 I/O 模型简介(图文)(转载)

    Linux 网络 I/O 模型简介(图文)(转载) 转载:http://blog.csdn.net/anxpp/article/details/51503329 1.介绍 Linux 的内核将所有外部 ...