Python中节省内存的方法之二:弱引用weakref

弱引用和引用计数息息相关,在介绍弱引用之前首先简单介绍一下引用计数。
引用计数
Python语言有垃圾自动回收机制,所谓垃圾就是没有被引用的对象。垃圾回收主要使用引用计数来标记清除。
引用计数:python中变量名和数据之间使用引用来建立联系。如a = [1,2,3]。列表[1,2,3]被变量a所引用,所以列表[1,2,3]的引用计数就是1。python中每一个对象都有引用计数。
可以通过sys模块的getrefcount获取某一个对象被引用计数的个数
>>> a = [1,2,3]
>>>
>>> import sys
>>> sys.getrefcount(a) # 由于方法本身也引用了变量a,所以个数为2。
2
>>>
>>> b = a
>>>
>>> sys.getrefcount(a)
3
>>>
b=a相当于新创建一个变量b指向[1,2,3]
垃圾回收:当某一个对象的引用计数等于0时就表明该对象没有被任何变量所引用,也就成为了内存垃圾,可以被垃圾回收机制所处理。
引用计数的特点:
- 当对象引用计数等于0时可以被回收;
- 当对象应用计数不等于0时不能被回收,除非触发手动回收;
弱引用
以上就是python中引用的基本知识,今天介绍的主角weakref(弱引用)就是和引用的机制非常密切的模块。弱引用就是不产生引用计数的特殊引用。
特性:
弱引用不会增加对象的引用数量。如果将引用的目标对象称为 指向对象(referent)。因此,弱引用不会妨碍所指对象被当作垃圾回收。
python中的弱引用会获取引用对象的地址,即可以调用对象对其进行相关操作,但是不会使引用的对象的引用计数增加,当引用对象的引用计数为0时,对象还是会被回收,弱引用也无法继续调用对象
- 弱应用可以操作指向对象的属性
- 弱应用不会增加指向对象的引用计数个数
适合场景:
结论:弱引用在缓存应用中很有用
有这样一个场景,如果一个缓存的字典中保存了key为id,value为某大型对象这样的键值对。当大型对象被删除del object之后,字典中保存的键值对依然不会被删除。因为字典存在,大型对象的引用计数会增加1。由于大型对象一直被引用,内存不能释放。
使用弱引用字典来保存如上的键值对,当大型对象删除时,缓存字典中的键值对也会被删除。能够有效释放内存。
weakref的使用
weakref.ref()使用
ref 的定义
class weakref.ref(object[, callback])
ref是用来构建弱引用最常见的函数,返回对对象的弱引用。
根据原始对象是否存活,返回值不同:
- 如果原始对象仍然存活,则可以通过调用引用对象来获得原始对象;
- 如果引用的原始对象不再存在,则调用引用对象将得到 None 。
- 支持传入回调函数,在原始对象即将终结时将调用回调。弱引用对象将作为回调的唯一参数传递
demo
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
demo = Demo(100)
demo_weakref = weakref.ref(demo) # 创建弱引用对象
print(demo_weakref()) # 通过调用函数的方法来调用,返回的是原始对象
>>> <__main__.Demo object at 0x7f21840e2e80>
print(demo_weakref() is demo) # 可以看出弱应用对象指向原始对象
>>> True
demo_weakref().get_value() # 调用原始对象的方法
>>> 100
del demo
print(demo_weakref()) # 删除原始对象之后,弱引用对象返回None
>>> None
弱应用对象不增加引用计数
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
demo = Demo(100)
print(sys.getrefcount(demo))
>>> 2
demo_weakref = weakref.ref(demo)
print(sys.getrefcount(demo))
>>> 2
注册函数的使用
通过ref构建弱引用对象时,可以传入回调函数,在原始对象销销毁时回调函数被调用。
需要注意回调函数的参数一定要传入弱引用对象
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
def notify_by_delete(weakref_obj):
print(f"{weakref_obj}注意:引用的对象被删除了")
demo = Demo(100)
demo_weakref = weakref.ref(demo, notify_by_delete)
demo_weakref().get_value()
del demo
weakref.proxy 的使用
ref在使用时需要显示调用才能获得原始对象,使用proxy返回原始对象的代理,使用代理对象可直接访问原始对象。
定义
weakref.proxy(object[, callback])
demo
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
demo = Demo(100)
demo_weakref = weakref.proxy(demo)
demo.get_value()
>>> 100
demo_weakref.get_value()
>>> 100
proxy相比ref省去了函数调用这一步,可以说使用更加方便。
WeakKeyDictionary
WeakKeyDictionary 是以弱引用对象为key的字典。
优点:创建一个key为弱引用的字典。优点是当key不在有引用计数时,key-value的映射会在字典中消失。
定义
weakref.WeakKeyDictionary([dict])
普通字典
在普通字典中,如果key是一个变量名,那么当变量被删除之后,字典中的key不会被删除。所以在一些场景中,如果是以对象作为key,那么删除对象之后需要字典中的key-value也能被删除,就可以使用弱引用对象。
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
Dict = {}
a = 100
Dict[a] = "一百"
print(Dict)
>>> {100: '一百'}
del a
print(Dict)
>>> {100: '一百'}
当变量a被删除之后,字典Dict中的key并不受影响。
弱引用字典 demo
wkdict = weakref.WeakKeyDictionary()
demo = Demo(200)
wkdict[demo] = "二百"
print(list(wkdict.items()))
del demo
print(list(wkdict.items()))
>>>
[(<__main__.Demo object at 0x10458b3a0>, '二百')]
[]
在弱引用字典中,key是一个对象,如果对象被删除之后,弱引用字典中的key-value键值对也会被删除。
以弱引用对象为value的字典 WeakValueDictionary
使用弱引用作为value的映射类:当不再有对value的强引用时,将丢弃字典中的条目。与weakref.WeakKeyDictionary功能类似,只不过作为弱引用的是value。功能类似,不再话下。
weakref.finalize
finalize 主要用来标志原始对象的销毁。finalize构建时传入一个函数,当原始对象被删除时会自动调用这个函数。
在原始对象被删除之前也可以手动调用finalize对象,但是其最多可以被调用一次,再次调用不会调用注册函数。
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
demo = Demo(100)
def delete_exec(x):
print('demo 被回收了..', "传入参数:", x)
res = weakref.finalize(demo, delete_exec, 200)
del demo
获取弱引用统计
想要获取一个对象的弱引用情况,可以通过getweakrefcount获取弱引用个数,getweakrefs获取弱引用的列表。
import sys
import weakref
class Demo():
def __init__(self, value):
self.a = value
def get_value(self):
print(self.a)
demo = Demo(100)
demo_weakref_one = weakref.ref(demo)
demo_weakref_two = weakref.proxy(demo)
count = weakref.getweakrefcount(demo)
print(count)
>>> 2
weak_list = weakref.getweakrefs(demo)
print(weak_list)
>>> [<weakref at 0x7f2e27c1d458; to 'Demo' at 0x7f2e27be0e80>, <weakproxy at 0x7f2e27b652c8 to Demo at 0x7f2e27be0e80>]
总结
总的来说弱引用有两个优点:
- 不占用引用计数,可节省内存
- 在原始对象被销毁时可以回调弱引用注册的回调函数
第一点在文中多处有强调,关于第二点是比较有特点的一个特性。可以使用第二点完成观察者模式,通知一些依赖某一个对象的所有对象。
Python中节省内存的方法之二:弱引用weakref的更多相关文章
- 查看python中模块的所有方法
查看python中模块的所有方法 安装的python模块,现将查看方法总结如下 一.CMD命令行下使用pydoc命令 在命令行下运行$ pydoc modules即可查看 二.在python交 ...
- PySpark 的背后原理--在Driver端,通过Py4j实现在Python中调用Java的方法.pyspark.executor 端一个Executor上同时运行多少个Task,就会有多少个对应的pyspark.worker进程。
PySpark 的背后原理 Spark主要是由Scala语言开发,为了方便和其他系统集成而不引入scala相关依赖,部分实现使用Java语言开发,例如External Shuffle Service等 ...
- python中requests库使用方法详解
目录 python中requests库使用方法详解 官方文档 什么是Requests 安装Requests库 基本的GET请求 带参数的GET请求 解析json 添加headers 基本POST请求 ...
- Python 中的内存管理
Python 中一切皆对象,这些对象的内存都是在运行时动态地在堆中进行分配的,就连 Python 虚拟机使用的栈也是在堆上模拟的.既然一切皆对象,那么在 Python 程序运行过程中对象的创建和释放就 ...
- python中List的sort方法的用法
python列表排序 简单记一下python中List的sort方法(或者sorted内建函数)的用法. 关键字: python列表排序 python字典排序 sorted List的元素可以是各种东 ...
- 【转】python中List的sort方法(或者sorted内建函数)的用法
原始出处:http://gaopenghigh.iteye.com/blog/1483864 python列表排序 简单记一下python中List的sort方法(或者sorted内建函数)的用法. ...
- 【转】关于python中re模块split方法的使用
注:最近在研究文本处理,需要用到正则切割文本,所以收索到了这篇文章,很有用,谢谢原作者. 原址:http://blog.sciencenet.cn/blog-314114-775285.html 关于 ...
- 全面了解python中的类,对象,方法,属性
全面了解python中的类,对象,方法,属性 python中一切皆为对象,所谓对象:我自己就是一个对象,我玩的电脑就是对象,坐着的椅子就是对象,家里养的小狗也是一个对象...... 我们通过描述属性( ...
- python中列表元素连接方法join用法实例
python中列表元素连接方法join用法实例 这篇文章主要介绍了python中列表元素连接方法join用法,实例分析了Python中join方法的使用技巧,非常具有实用价值,分享给大家供大家参考. ...
- python 中去除空格的方法
python 中去除空格的方法: def trim(s): l=[] for i in s: if i!=' ': l.append(i) return ''.join(l) 其中可以使用下面的 '' ...
随机推荐
- Welcome to YARP - 5.身份验证和授权
目录 Welcome to YARP - 1.认识YARP并搭建反向代理服务 Welcome to YARP - 2.配置功能 2.1 - 配置文件(Configuration Files) 2.2 ...
- rust程序设计(5)结构体相关练习题| 附带解答
题目 基础结构体练习: 创建一个名为Person的结构体,包含name(字符串类型)和age(整数类型)两个字段. 写一个函数,接收一个Person实例作为参数,并打印出这个人的名字和年龄. 结构体方 ...
- UIPath流程控制
应当仔细地观察,为的是理解:应当努力地理解,为的是行动. UIPath程序中流程控制主要包括条件语句.循环语句以及中断语句.下面我们一一学习这些语句在RPA流程设计中的使用. 1. 条件判断 if ...
- Linux-目录层次标准
版权声明:原创作品,谢绝转载!否则将追究法律责任. ----- 作者:kirin 根目录(/) 根目录是整个系统最重要的一个目录,因为不但所有的目录都是由根目录衍生出来的,同时根目录也与开机.还原.系 ...
- 如何检测Windows服务停止后自动启动?自动运行.bat批处理文件?
作者:西瓜程序猿 主页传送门:https://www.cnblogs.com/kimiliucn 前言 想要确保你的Windows服务即使在崩溃后也能自动重启吗?这篇文章教你如何用一个小巧的批处理脚本 ...
- java文件读取 while ((len = reader.read(buffer)) != -1){}的理解
源文件 InputStreamReader reader=new InputStreamReader(new FileInputStream(file),"UTF-8"); //读 ...
- KR4-KP4
serdes测试中经常遇到KR4 和KP4, KR4指的是FEC 528 514对应25X4的100G KP4对应FEC 544 514,56/100x4 的200/400G链路 KP4应用比较广泛, ...
- [ABC263C] Monotonically Increasing
Notes For two integer sequences of the same length $A_1,A_2,\dots,A_N$ and $B_1,B_2,\dots,B_N$, $A$ ...
- [转载] Winform WebBrowser 使用 Edge 内核
原文地址 C# 设置 WebBrowser 使用 Edge 内核_c# webbrowser 内核 - CSDN 博客 原文内容 1. 问题描述 用 C# 写了一个小工具, 需要显示网页上的内容, 但 ...
- TensorFlow C++ 初始化 Tensor 内存 到GPU 内存
最近使用TensorFlow C++版本实现神经网络的部署,我通过GPU 处理得到网络的输入值,因此输入值在GPU内存上保存, TF 输入tensor 的调用语句为 Tensor inputTenso ...