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

引用计数

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. Lyndon 分解

    介绍 [模板]Lyndon 分解 #include<cstdio> #include<cstring> char s[5000005]; int main(){ scanf(& ...

  2. 多数元素 (3.12 leetcode每日打卡)

    给定一个大小为 n 的数组,找到其中的多数元素.多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的,并且给定的数组总是存在多数元素. 示例 1: 输入: [3,2,3] ...

  3. Windows 11 + Samsung 980 踩坑:在 LocalDB 15.0 实例启动期间出错: 无法启动 SQL Server 进程(附赠 查询指定日期范围内的前1000条SQL执行记录)

    Windows 11 + Samsung 980 踩坑:在 LocalDB 实例启动期间出错: 无法启动 SQL Server 进程 起因 用 Microsoft Visual Studio 2022 ...

  4. 🔥🔥Java开发者的Python快速进修指南:实战之简易跳表

    前言 之前我已经将Python的基本语法与Java进行了比较,相信大家对Python也有了一定的了解.我不会选择去写一些无用的业务逻辑来加强对Python的理解.相反,我更喜欢通过编写一些数据结构和算 ...

  5. Http的演进

    Http的演进 Http在1.1版本之前具有无状态的特点,每次请求都需要通过TCP三次握手四次挥手与服务器重新建立连接.比如某个客户端在短时间多次请求同一个资源,服务器并不能区别是否已经响应过用户请求 ...

  6. springboot——入门案例

    真简单啊 springboot 学了入门案例,有感而发 首先是一个自带的配置文件: package com.example.springboot_01; import org.springframew ...

  7. 解决win10的wifi打不开或无法搜索到周围wifi的问题

    今天笔者遇到了一个比较奇葩的问题,就是笔记本电脑的wifi打不开了,即使打开了也是搜索不到周围的wifi的.这个问题一开始笔者没有发现,因为在暑假期间都是使用笔记本连接自己的手机热点进行上网的.然而暑 ...

  8. springboot产生非法状态异常+空指针

    springboot产生非法状态异常+空指针 描述:异常描述为在响应提交后不能执行senderror方法 解决方案: 不是,哥们,你不会真觉得有什么合适的解决方案吧,网上几波前辈大佬各说各的,和我的情 ...

  9. ElasticSearch之Slow Log

    ElasticSearch的慢日志,相关的参数及配置方法. 在log4j2.properties中配置慢日志的输出文件名. Search Slow Log 相关参数 index.search.slow ...

  10. uniapp的app苹果应用商店上架最简教程

    除了测试版本之外,uniapp打包好的ipa文件是无法直接安装在普通用户的手机上面,这是苹果的证书和描述文件的机制的原因. 因此我们需要将打包好的ipa文件上架到苹果应用商店,也就是app store ...