首页 > 编程语言 >Python Weakref弱引用

Python Weakref弱引用

时间:2023-10-04 20:44:30浏览次数:57  
标签:Python self Weakref 对象 引用 key data wr

背景

在开始讨论弱引用的实现之前,我们先来看看什么是弱引用以及弱引用的作用。

在我们平常编写代码的时候我们经常会使用引用,了解一下Python,我们就会知道,当一个对象的引用为0时,才会销毁该对象。

那么比如我们现在需要维护这样一个东西:
不断向一个多线程程序发送数据,同时引入一个缓存来存储数据,但是当没有人使用该数据的时候,销毁这个缓存来节省空间。

那么一个很显然的思路是建立一个 dict,然后不断往 dict 里面添加信息,这时候我们就会注意到这样的问题:dict 存储的信息都是引用,当我们使用完了数据,还是会留下一个dict中的引用,这样数据并不会被销毁,而且会越堆积越多。

示例代码如下:

import threading


class Data:
    def __init__(self, key):
        pass


class Cacher:
    def __init__(self):
        self.pool = {}
        self.lock = threading.Lock

    def get(self, key):
        with self.lock:
            data = self.pool.get(key)
            if data:
                return data
            self.pool[key] = data = Data(key)
            return data

如何解决引用带来的内存占用

那么这个时候就要用到我们的弱引用了。

弱引用并不会增加引用数,但是它会关联目标对象。
如图所示
弱引用示例图

当我们删除引用时,就会变成如下情况
删除引用示例图

这样一来,我们只需要把缓存改成弱引用即可。

class Cacher:
    def __init__(self):
        self.pool = {}
        self.lock = threading.Lock

    def get(self, key):
        with self.lock:
            data = self.pool.get(key)
            if data:
                return data
            data = Data(key)
            self.pool[key] = weakref.ref(data)
            return data

由于缓存 dict 只保存 Data 对象的弱引用,因此 Cacher 不会影响 Data 对象的引用计数,当所有线程用完之后,引用计数就归零,数据就会被释放。

实际上用字典缓存数据对象很常见,所以weakref模块还提供了两种只保存弱引用的字典对象:

  • weakref.WeakKeyDictionary 键只保存弱引用
  • weakref.WeakValueDictionary 值只保存弱引用

所以我们也可以通过把 dict 改成 WeakValueDictionary 来实现我们想要的功能

class Cacher:
    def __init__(self):
        self.pool = weakref.WeakValueDictionary()
        self.lock = threading.Lock

    def get(self, key):
        with self.lock:
            data = self.pool.get(key)
            if data:
                return data
            self.pool[key] = data = Data(key)
            return data

工作原理

然后我们就可以去阅读源代码了,笔者这里直接找到了 CPython 实现

注意:3.11以后的代码中的单独有一个头文件来做 typedef,读者不必纠结 PyObecjt 从哪里来
笔者所看的为3.11 版本号c8de883bcb022b59d7ae6d20993599b47e4b968b

在 /Objects/weakrefobject.c 和 /Include/weakrefobject.h 中
很容易就可以发现 PyWeakReference 包含如下字段:
PyWeakReference字段

我们主要解释 wr_object, wr_callback, hash, wr_prevwr_next

  • wr_object 是对象指针,指向被引用的对象,弱引用根据该字段找到被引用的对象,但是不会产生引用。
  • wr_callback 指向一个可调用对象,当被引用的对象销毁时调用
  • hash 缓存被引用对象的哈希值
  • wr_prevwr_next 分别是前后向指针,用于将弱引用对象组织成双向链表。

所以一个对象的弱引用组织结构如下
弱引用组织结构

  • 弱引用对象通过 wr_object 字段来关联被引用对象,就像上面图中红色箭头一样
  • 一个对象可以同时被多个弱引用对象关联,图中的 Data 实例被两个弱引用对象关联 weakrefobject.c 943行
  • 当一个对象被销毁了之后,Python将遍历一遍弱引用链表,逐一处理。
    • wr_object 设置为 None 具体是现在 weakrefobject.c 中第55行
    • 执行回调函数 wr_callback(如果有)

由此可见,弱引用的工作原理就是设计模式中的 观察者模式。当对象被销毁,所有的弱引用对象都得到通知并进行处理。

一些实现细节后面更新……

标签:Python,self,Weakref,对象,引用,key,data,wr
From: https://www.cnblogs.com/Gensokyo-Alice/p/17742717.html

相关文章

  • Python中的变量
    Python中的变量变量的定义程序中,数据都临时存储在内存中。每一个被存储在内存的数据都有一个内存地址。其中特定的数据被我们所使用,因此我们为那些内存地址定义了名称。这一名称被称作标识符,又称变量名。而与变量名对应内存地址中的数据被称为变量值。总结:变量为内存中特定的......
  • python_day3
    Python_day31、列表index=0whileindex<len(列表): 元素=列表[index]对元素进行处理index+=1方法2:for临时变量in数据容器: 对临时变量进行处理while循环可以控制循环条件,但是for循环不行2、元组元组一旦被定义就不能被修改元组:使用小括号,且使......
  • 笨办法学Python3 习题20 函数和文件
    脚本函数运行内容:系统模块导入参数变量解包参数变量(脚本,文件变量1)定义函数1,执行读取文件定义函数2,执行读取位置移动到文本开头定义函数3(参数1,参数2),执行打印参数1,读取参数2的一行打开文件变量1赋值刚创的文档变量调用函数1,读取文件调用函数2,读取位置移动到文本开头......
  • 笨办法学Python3 习题19 函数和变量
    定义调用函数:全局变量名和函数变量名避免取相同的名字函数之外的变量不会改变函数中原来的变量,调用函数时会被临时创建,函数运行完就会被丢弃定义函数(参数变量1,参数变量2):    函数命令打印你有参数变量1的干酪    打印你有参数变量2的干酪盒子   ......
  • 笨办法学Python3 习题18 命名、变量、代码和函数
    知识点:定义函数的格式def函数名1(参数1,参数2):   函数命令def函数名2(*args):   解包   函数命令def函数名3(参数1):   函数命令def函数名4():  # 可以是无参数的函数   函数命令函数名1(参数值,参数值)#调用函数1函数名2(参数值,参数值)#......
  • python多进程:fork模式和spawn模式
    python多进程:fork模式和spawn模式fork模式1.仅unix系统支持,并且是unix系统的默认模式.2.使用该模式创建子进程的时候,会复制父进程的全部变量,支持传参(任意类型)给子进程,但是不会复制父进程的线程.3.该模式相当于将父进程的内存复制一份用于创建子进程.但是由于不复制线程......
  • 笨办法学Python3 习题17 更多文件操作
    脚本运行内容:系统模块导入参数变量导入os.path模块调用exists函数  解包参数变量(脚本,被复制文件1,粘贴到文件2)打印语句,需要复制文件1内容到文件2打开文件1读取文件1打印语句,中间用到格式化,len(X)函数来运算文件1中字符长度打印语句,中间用到格式化,exists(Y)函数......
  • 流畅的python笔记 (二) 2.序列构成的数组
    内置序列类型分类1:容器序列(能存放不同类型):list,tuple,collections.deque扁平序列(不能存放不同类型):str,bytes,bytearray,memoryview,array.array分类2:可变序列(能被修改):list,bytearray,array.array,collections.deque,memoryview不可变序列:tuple,str,bytes列表推导......
  • python练习2 | 类的继承
    点击查看代码#类继承练习:人力系统#员工分为两类,全职员工FullTimeEmployee、兼职员工PartTimeEmployee#全职和兼职都有”姓名,name,工号:id属性#都具备打印信息print_info(打印姓名、工号)方法#全职有月薪monthly_salary属性#兼职有日薪daily_salary属性,每月工作天数......
  • Python笔记
    第一章、Python概述1.1 扩展库安装方法使用pip命令安装扩展库。在cmd命令行中输入pip,回车后可以看到pip命令的使用说明。1.2 常用的pip命令pip命令示例说明pipfreeze[>requirements.txt]列出已安装扩展库及其版本号(不知道怎么用。。?)pipinstallSomePacka......