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

引用计数

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. Ynoi 题目总结:

    Preview: 因为是关于 \(\text{Ynoi}\) 的题目总结,所以自然也要像 \(\text{Ynoi}\) 一样来一段长长的开头. 这是一个蒟蒻的告白 高一上半学期,嘻嘻哈哈的糊弄过去了 ...

  2. 自定义Graph Component:1-开发指南

      可以使用自定义NLU组件和策略扩展Rasa,本文提供了如何开发自己的自定义Graph Component指南.   Rasa提供各种开箱即用的NLU组件和策略.可以使用自定义Graph Compo ...

  3. L2-039 清点代码库

    #include <bits/stdc++.h> using namespace std; const int N = 10010, M = 110; int main() { int n ...

  4. 低代码平台解密:探秘MQTT协议的应用之道

    前言 低代码平台作为当今快速发展的技术之一,为开发人员提供了更高效.更简便的工具和方法,以快速构建和部署应用程序.而MQTT协议作为物联网领域的重要通信协议,在低代码平台上的应用也日益受到关注,今天小 ...

  5. Vue04-vue-router

    vue-router 目前前端流行的三大框架, 都有自己的路由实现: Angular:ngRouter React:ReactRouter Vue:vue-router Vue Router 是 Vu ...

  6. 【Spring Boot】【外包杯】学习day01 | 项目目录结构划分以及代码分层

    起因:扒了一个开源的项目,但是啃起来很硬,所以决定开始学习相关的知识. 我们之前的SSM项目,搭建过程较为繁琐: 1)配置 web.xml,加载 spring 和 spring mvc 2)配置数据库 ...

  7. AntDesignBlazor示例——新建项目

    本示例是AntDesign Blazor的入门示例,在学习的同时分享出来,以供新手参考. 1. 开发环境 VS2022 17.8.2 .NET8 AntDesign 0.16.2 2. 学习目标 创建 ...

  8. java-图片添加水印

    前言:    需求:需要在图片中添加水印,防止盗用   优缺点:     优点:保护版权,防止盗用     缺点 可能会影响图片的视觉效果:如果水印过大或过醒目,可能会影响图片的视觉效果. 可能会增加 ...

  9. ASP.NET Core 8 在 Windows 上各种部署模型的性能测试

    ASP.NET Core 8 在 Windows 上各种部署模型的性能测试 我们知道 Asp.net Core 在 windows 服务器上部署的方案有 4 种之多.这些部署方案对性能的影响一直以来都 ...

  10. Markdown References

    每次写文章,用到都要查一下的Markdown语法 字体颜色 [Markdown笔记]设置字体颜色 字体颜色 Font colors Decrsiption Demonstration Effect 选 ...