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

引用计数

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. Java 面试题之 Logback 打印日志是如何获取当前方法名称的?

    在 Java 中,有四种方法可以获取当前正在执行方法体的方法名称,分别是: 使用 Thread.currentThread().getStackTrace() 方法 使用异常对象的 getStackT ...

  2. python循环之for循环

    当我们想让列表中的元素一个一个打印出时,用多个print()就显得代码很繁琐了,这是我们就可以用到循环 list_1 = ['one', 'two', 'three', 'four', 'five'] ...

  3. 手把手教Electron+vue,打包vue项目,打包成桌面程序。

    1.现如今前端框架数不胜数,尤其是angular.vue吸引一大批前端开发者,在这个高新技术快速崛起的时代,自然少不了各种框架的结合使用.接下来是介绍electron+vue的结合使用. 2.Elec ...

  4. 【Javaweb】implements Serializable是什么意思?反序列化是什么意思?

    为了保证数据传输的可靠 性,常常要implements Serializable,为什么? 对象本质上是虚无缥缈的,只是内存中的一个地址,如果想要让对象持久化,让对象在网络上传输,总不可能传送一个内存 ...

  5. JSR223取样器详解

    相比于BeanShell 取样器,JSR223取样器具有可大大提高性能的功能(编译)如果需要,一定要使用JSR223取样器编写脚本是更好的选择!!! 属性描述名称:显示的此取样器的描述性名称,可自定义 ...

  6. [gym104076][CCPC2022济南站L] Tree Distance

    You are given an unrooted weighted tree \(T\) with vertices \(1,2,-,n\). Please answer some queries. ...

  7. [USACO2007FEB S]Silver Lilypad Pond

    题目描述 为了让奶牛们娱乐和锻炼,农夫约翰建造了一个美丽的池塘.这个长方形的池子被分成 了M行N列个方格(1 ≤ M, N ≤ 30).一些格子是坚固得令人惊讶的莲花,还有一些格子是 岩石,其余的只是 ...

  8. Feign远程调用丢失请求头问题

    问题 项目中我使用feign进行远程调用时,在请求头设置了参数(UserId),结果在调用方却收获取不到... 解决办法 feign提供了一个拦截器,在远程调用之前执行 核心代码 package co ...

  9. 被面试官PUA了:创建索引时一定会锁表?

    索引主要是用于提高数据检索速度的一种机制,通过索引数据库可以快速定位到目标数据的位置,而不需要遍历整个数据集,它就像书籍的目录部分,有它的存在,可以大大加速查询的效率. 那么问题来了:在创建索引时一定 ...

  10. GPT-4多模态大型语言模型发布

    GPT-4 模型是OpenAI开发的第四代大型语言模型(LLM),它将是一个多模态模型,会提供完全不同的可能性-例如文字转图像.音乐甚至视频.GPT 全称为 Generative Pre-traine ...