弱引用和引用计数息息相关,在介绍弱引用之前首先简单介绍一下引用计数。

引用计数

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时就表明该对象没有被任何变量所引用,也就成为了内存垃圾,可以被垃圾回收机制所处理。

引用计数的特点:

  1. 当对象引用计数等于0时可以被回收;
  2. 当对象应用计数不等于0时不能被回收,除非触发手动回收;

弱引用

以上就是python中引用的基本知识,今天介绍的主角weakref(弱引用)就是和引用的机制非常密切的模块。弱引用就是不产生引用计数的特殊引用。

特性

弱引用不会增加对象的引用数量。如果将引用的目标对象称为 指向对象(referent)。因此,弱引用不会妨碍所指对象被当作垃圾回收。

python中的弱引用会获取引用对象的地址,即可以调用对象对其进行相关操作,但是不会使引用的对象的引用计数增加,当引用对象的引用计数为0时,对象还是会被回收,弱引用也无法继续调用对象

  1. 弱应用可以操作指向对象的属性
  2. 弱应用不会增加指向对象的引用计数个数

适合场景

结论:弱引用在缓存应用中很有用

有这样一个场景,如果一个缓存的字典中保存了key为id,value为某大型对象这样的键值对。当大型对象被删除del object之后,字典中保存的键值对依然不会被删除。因为字典存在,大型对象的引用计数会增加1。由于大型对象一直被引用,内存不能释放。

使用弱引用字典来保存如上的键值对,当大型对象删除时,缓存字典中的键值对也会被删除。能够有效释放内存。

weakref的使用

weakref.ref()使用

ref 的定义

class weakref.ref(object[, callback])

ref是用来构建弱引用最常见的函数,返回对对象的弱引用。

根据原始对象是否存活,返回值不同:

  1. 如果原始对象仍然存活,则可以通过调用引用对象来获得原始对象;
  2. 如果引用的原始对象不再存在,则调用引用对象将得到 None 。
  3. 支持传入回调函数,在原始对象即将终结时将调用回调。弱引用对象将作为回调的唯一参数传递

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>]

总结

总的来说弱引用有两个优点:

  1. 不占用引用计数,可节省内存
  2. 在原始对象被销毁时可以回调弱引用注册的回调函数

第一点在文中多处有强调,关于第二点是比较有特点的一个特性。可以使用第二点完成观察者模式,通知一些依赖某一个对象的所有对象。

Python中节省内存的方法之二:弱引用weakref的更多相关文章

  1. 查看python中模块的所有方法

    查看python中模块的所有方法     安装的python模块,现将查看方法总结如下 一.CMD命令行下使用pydoc命令 在命令行下运行$ pydoc modules即可查看 二.在python交 ...

  2. PySpark 的背后原理--在Driver端,通过Py4j实现在Python中调用Java的方法.pyspark.executor 端一个Executor上同时运行多少个Task,就会有多少个对应的pyspark.worker进程。

    PySpark 的背后原理 Spark主要是由Scala语言开发,为了方便和其他系统集成而不引入scala相关依赖,部分实现使用Java语言开发,例如External Shuffle Service等 ...

  3. python中requests库使用方法详解

    目录 python中requests库使用方法详解 官方文档 什么是Requests 安装Requests库 基本的GET请求 带参数的GET请求 解析json 添加headers 基本POST请求 ...

  4. Python 中的内存管理

    Python 中一切皆对象,这些对象的内存都是在运行时动态地在堆中进行分配的,就连 Python 虚拟机使用的栈也是在堆上模拟的.既然一切皆对象,那么在 Python 程序运行过程中对象的创建和释放就 ...

  5. python中List的sort方法的用法

    python列表排序 简单记一下python中List的sort方法(或者sorted内建函数)的用法. 关键字: python列表排序 python字典排序 sorted List的元素可以是各种东 ...

  6. 【转】python中List的sort方法(或者sorted内建函数)的用法

    原始出处:http://gaopenghigh.iteye.com/blog/1483864 python列表排序 简单记一下python中List的sort方法(或者sorted内建函数)的用法. ...

  7. 【转】关于python中re模块split方法的使用

    注:最近在研究文本处理,需要用到正则切割文本,所以收索到了这篇文章,很有用,谢谢原作者. 原址:http://blog.sciencenet.cn/blog-314114-775285.html 关于 ...

  8. 全面了解python中的类,对象,方法,属性

    全面了解python中的类,对象,方法,属性 python中一切皆为对象,所谓对象:我自己就是一个对象,我玩的电脑就是对象,坐着的椅子就是对象,家里养的小狗也是一个对象...... 我们通过描述属性( ...

  9. python中列表元素连接方法join用法实例

    python中列表元素连接方法join用法实例 这篇文章主要介绍了python中列表元素连接方法join用法,实例分析了Python中join方法的使用技巧,非常具有实用价值,分享给大家供大家参考. ...

  10. python 中去除空格的方法

    python 中去除空格的方法: def trim(s): l=[] for i in s: if i!=' ': l.append(i) return ''.join(l) 其中可以使用下面的 '' ...

随机推荐

  1. 解决IDEA中.properties文件中文变问号(???)的问题(已解决)

    问题背景 构建SpringBoot项目时,项目结构中有一个application.properties文件.这个项目是Spring Boot一个特有的配置文件.内容如下(我写了一些日志的配置): 写到 ...

  2. 2023浙江省大学生信息安全竞赛决赛 Cry+Misc wp

    搞到了一些附件,做做看难度如何. CRYPTO R_r 1.题目信息 查看代码 from Crypto.Util.number import * import random from gmpy2 im ...

  3. Vue 项目部署到GitHub Pages并同步到Gitee Pages

    前言:相信很多前端开发者都拥有自己的vue项目,若想把自己的项目做成网站分享给大家看,最常用的就是利用Github提供的GitHub Pages服务和Gitee提供的Gitee Pages服务.其中, ...

  4. Codeforces Round #707 (Div. 2)A~C题解

    写在前边 链接:Codeforces Round #707 (Div. 2) 心态真的越来越不好了,看A没看懂,赛后模拟了一遍就过了,B很简单,但是漏了个判断重复的条件. A. Alexey and ...

  5. Ubuntu 20.04 挂载局域网络共享硬盘

    创建挂载目录 mkdir /media/nas 创建认证文件.若无密码可以忽略这一步. sudo vim /root/.examplecredentials 按照以下格式写入用户名密码: userna ...

  6. Macbook磁盘系统结构/文件/目录介绍分析

    1. 系统磁盘根目录详解: 1.1 磁盘根目录结构 / (根目录)|-- Applications # 存放应用程序|-- Users # 存放用户文件和设置|-- cores # 存放核心转储文件, ...

  7. 【流式传输】使用Spring Boot实现ChatGpt流式传输

    引言 在ChatGpt火了这么久,他的那种单字单字返回的格式可能让很多朋友感到好奇,在之前我用c#写了一个版本的,同时支持IAsyncEnumerable以及SSE,今天把之前写的Java版本的也发出 ...

  8. OpenEuler22.03安装PostgreSQL15.5并配置一主二从

    环境准备 序号 IP 标识(hostname) CPU/内存配置 系统盘 数据盘 1 192.168.8.190 pg01 8C+16G 80G 500G 2 192.168.8.191 pg02 8 ...

  9. 珍藏网站-关于路由器、WIFI协议等

    路由器详解:为什么不要买AX3000路由器 https://zhuanlan.zhihu.com/p/403855533 包含以下专业名词和相关话题: RX/TX和MU-MIMO 20MHz/40MH ...

  10. python脚本抢大麦网演唱会门票 ---保姆级教程 python脚本抢大麦网演唱会门票

    python脚本抢大麦网演唱会门票 流程: 1.下载并安装anaconda:https://repo.continuum.io/archive/ 下载对应linux/mac/windows版本 2.下 ...