介绍

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. LAMP之Apache调优

    一.环境的搭建 实验环境: 操作系统:Centos 7.4 [root@xuegod70 ~]# ls apr-1.6.3.tar.gz apr-util-1.6.1.tar.bz2 httpd-2. ...

  2. sudo: unable to execute ./xxx.py: no such file or directory

    $ ./mk_dataset.py -bash: ./mk_dataset.py: /home/ocean1101/anaconda3/bin/python^M: bad interpreter: N ...

  3. c/c++编码规范(1)--头文件

    最近工作稍微轻松一点,就再学习了一下编码规范.遂记些笔记,以便查阅. 这次学习的是Google 开源项目风格指南中文版,地址是:http://zh-google-styleguide.readthed ...

  4. Code First EF 多对多时拆分两个一对多

    (*)多对多中还可以为中间表建立一个实体方式映射.当然如果中间关系表还想有其他字段,则要必须为中间表建立实体类(中间表和两个表之间就是两个一对多的关系了). demo项目 WebApp22 GitHu ...

  5. .NET Core WebApi中返回 json 数据首字母大小写问题

    public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddJsonOptions(opt =& ...

  6. Fabric1.4 链码开发,开发模式下的测试

    关闭之前已启动的网络环境 sudo docker-compose -f docker-compose-cli.yaml down 进入devmode目录:  cd ~/go/src/github.co ...

  7. springboot整合es客户端操作elasticsearch(五)

    springboot整合es客户端操作elasticsearch的总结: 客户端可以进行可以对所有文档进行查询,就是不加任何条件: SearchRequest searchRequest = new ...

  8. Autoit安装及启动

    1.Autoit下载: 官网下载地址:https://www.autoitscript.com/site/autoit/downloads/ 提供百度网盘下载:https://pan.baidu.co ...

  9. cm api

    cm API:https://github.com/cloudera/cm_api/tree/master/python/examples/auto-deploy#看集群有几个clustercurl ...

  10. CWMP开源代码研究7——cwmp移植

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...