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) 其中可以使用下面的 '' ...
随机推荐
- shopify本地开发 新的地方
看了一些教程有些旧地方通不过,自己摸索下 1.创建app 点击Settings,再点击Apps and sales channels 点Allow custom app development 继续 ...
- 算法训练 递归 s01串
问题描述 s01串初始为"0" 按以下方式变换 0变1,1变01 输入格式 1个整数(0~19) 输出格式 n次变换后s01串 样例输入 3 样例输出 101 数据规模和约定 0~ ...
- Android Gson 混淆问题
开发过程中遇到一个奇怪的问题. 有一个接口,debug 版本接收到云侧下发的字符串后可以通过 gson 将其转换为相应 bean 类,而 release 版本拿到的 bean 总是缺少一个关键的字段, ...
- React 中虚拟DOM是什么,为什么需要它?
注意:本节主要讲React中的虚拟DOM,但是虚拟DOM并不是React中特有的内容. 1. React 中虚拟 DOM是什么? 虚拟DOM是对真实DOM的描述,虚拟DOM是JS对象,实际上就是 JS ...
- [2020-2021 集训队作业] Tom & Jerry
题目背景 自选题 by ix35 题目描述 给定一张包含 \(n\) 个顶点和 \(m\) 条边的 无向连通图,Tom 和 Jerry 在图上进行了 \(q\) 次追逐游戏. 在第 \(i\) 次游戏 ...
- 【matlab混沌理论】1.2.洛伦兹吸引子
Lorenz洛伦兹吸引子定义洛伦兹函数组后,通过ode45函数求解此微分方程方程. input: % Lorenz函数的洛伦兹吸引子 % 2.定义模型参数 sigma = 10; beta = 8/ ...
- 安装MacOS - 初学者系列 - 学习者系列文章
上次安装了个VMWare Workstation 17.5.0的精简注册版,昨天打开软件,发现竟然支持了MacOS操作系统了,所以就想把安装的过程记录一下. 1. 准备虚拟机: 1) 下载精简版: 从 ...
- SpringCloudGateway解决跨域问题
1.跨域问题详情 2.为什么会跨域? 官方定义:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS 怎么出现的? 1.浏览器访问了一个业务 h ...
- 探究vue的diff算法
1.diff算法是什么? diff算法是一种通过**同层的树节点**进行比较的高效算法 Diff 算法探讨的就是虚拟 DOM 树发生变化后,生成 DOM 树更新补丁的方式.对比新旧两株虚拟 DOM 树 ...
- hszxoj 货车运输 [lca]
题目链接: hszxoj 货车运输 题目描述与思路 简化题目: 求 \(x\)到 \(y\) 两点间路径的边权最小值的最大值 与之前的最短路最大的不同是这道题是多源最短路,那么 \(spfa\) 就废 ...