Python中的弱引用与基础类型支持情况探究
背景
最近有一个业务场景需要用Python自行实现一个简单的LRU cache,不可避免的接触到了弱引用这一概念,这里记录一下。
强引用
Python内存回收由垃圾回收器自动管理,当一个对象的引用计数归0时,其内存就可能被回收掉,而引用计数器的数值其实就是代表有多少个强引用指向该对象,我们日常写的Python代码如果没有使用到weakref模块一般都只会涉及到强引用。
可以通过sys.getrefcount查看对象的引用计数,如以下代码:
import sys
alist = [1, 2, 3] # alist引用计数=1
print(sys.getrefcount(alist)) # 包括getrefcount本身新增的强引用,输出2
blist = alist
print(sys.getrefcount(alist)) # 新增blist强引用,输出3
print(blist) # 输出[1, 2, 3]
del blist
print(sys.getrefcount(alist)) # 删除了blist,强引用-1, 输出2
弱引用
与强引用相对,弱引用并不会影响对象的引用计数,也就是说其不影响对象是否被回收的判定,如以下代码:
import sys
import weakref
class tlist(list): # list本身不支持弱引用,但其子类支持
pass
alist = tlist([1, 2, 3]) # alist引用计数=1
print(sys.getrefcount(alist)) # 输出2
bref = weakref.ref(alist) # bref为对alist对象的弱引用
print(bref()) # 返回弱引用对象,输出: [1, 2, 3]
print(sys.getrefcount(alist)) # 由于弱引用不影响引用计数,依然输出2
del alist # 删除alist,对象引用计数变为0
print(bref()) # 由于bref指向的对象已无任何强引用,返回None
如上代码所示弱引用不会影响对象的引用计数,亦即不会影响对象内存的回收,但是这里碰到一个引人疑惑的点,就是Python中的基本数据类型对弱引用的支持分了三种情况。
基础类型对于弱引用支持情况
基础类型int、list、dict、tuple、str不支持弱引用,对其执行弱引用会报错:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-9daeb515714d> in <module>
----> 1 weakref.ref(alist)
TypeError: cannot create weak reference to 'list' object
可以通过__weakrefoffset__查看类型是否支持弱引用,该变量表示弱引用指针相对对象起始地址的偏移量,>0表示支持弱引用:
In [1]: int.__weakrefoffset__
Out[1]: 0
In [2]: str.__weakrefoffset__
Out[2]: 0
In [3]: tuple.__weakrefoffset__
Out[3]: 0
In [4]: list.__weakrefoffset__
Out[4]: 0
In [5]: dict.__weakrefoffset__
Out[5]: 0
In [6]: set.__weakrefoffset__
Out[6]: 192
官方文档中介绍:
Several built-in types such as list and dict do not directly support weak references but can add support through subclassing:
CPython implementation detail: Other built-in types such as tuple and int do not support weak references even when subclassed.
总结基础类型对弱引用的支持分为以下三种情况(for python3.8):
- 对于list、dict、str本身不支持弱引用,但可以通过创建子类的方式对其进行弱引用
- 对于int、tuple本身及其子类均不支持弱引用
- set直接支持弱引用
这又是出于什么考虑?通过一番探究得出以下可能原因:
- 绝大部分场景下,基础类型使用并不涉及到弱引用,所以基础类型不支持弱引用可以有效避免相应的overhead。
- 弱引用添加于Python2.1,所以对于之后添加的类型(包括object、type、set等)默认都是支持弱引用的,除非有明确的理由不这么做。
- 对于list、dict、int、str、tuple这些2.1之前的基础类型为了兼容性考虑均默认不支持弱引用,而set添加与2.3,因此其直接支持弱引用。
- int、str、tuple这些不可变对象,在CPython解释器中会有特殊的处理逻辑:
4.1 如[-5, 256]范围的小整数池一开始就被创建好了,在程序的整个生命周期无论是否被实际引用都不会被回收。
4.2 又如对于同一个compilation unit的tuple对象,如果取值相同,编译器会将独立的多个相同的tuple对象处理为指向同一个对象的多个强引用。
在这些情况下使用弱引用并没有什么明显的好处,反而额外引入了overhead,综合考虑直接对其不支持弱引用。 - 出于CPython的具体实现细节,对于int、tuple的子类也不支持弱引用。
转载请注明出处,原文地址:https://www.cnblogs.com/AcAc-t/p/python_weakref_study.html
参考
https://docs.python.org/3.8/library/weakref.html
https://www.cnblogs.com/marsggbo/p/14831456.html
https://www.cnblogs.com/AcAc-t/p/python_weakref_study.html
https://stackoverflow.com/questions/52011430/python-which-types-support-weak-references
Python中的弱引用与基础类型支持情况探究的更多相关文章
- Python中的变量引用对象需注意的几点
Python中的变量引用对象需注意的几点 分类:Python (55) (0) 普通引用: Python中,变量的作用仅仅是一个标识,只有赋值后才被创建,它可以引用任何类型的对象,而且在引用之前必须 ...
- 理解Java中的弱引用(Weak Reference)
本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...
- C# 中的弱引用 WeakReference
C#中的弱引用(WeakReference) 我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的.我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET ...
- WeakHashMap<K,V> 中的弱引用
相信很多人对WeakHashMap并没有完全理解. WeakHashMap 持有的弱引用的 Key. 1. 弱引用的概念: 弱引用是用来描述非必需对象的,被弱引用关联的对象只能生存到下一次垃圾收集发生 ...
- python中是否有单独的字符类型,通过下标的方式表示字符串中的字符
说明: 在python中,没有单独的字符类型,一个字符呢就是一个大小为1的字符串. 并且可以通过下标的方式,表示字符串中的字符. 操作过程: 1.通过[ ]的方式表示字符串中的第几个字符 >&g ...
- python中常见的一些错误异常类型
python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误.你可以使用该功能来调试python程序. 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的 ...
- 简单说说.Net中的弱引用
弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat kitty = new Cat(); 变量ki ...
- .NET中的弱引用
弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat cat = new Cat(); 变量cat就 ...
- Java中的弱引用
Strong references StringBuffer buffer = new StringBuffer(); 普通的对象创建都是这种类型,只要buffer还存在,对象就不会被GC回收.同时也 ...
- Python中请使用isinstance()判断变量类型
一.isinstance() 在Python中可以使用type()与isinstance()这两个函数判断对象类型,而isinstance()函数的使用上比type更加方便. # coding=utf ...
随机推荐
- 图像I、P、B帧介绍
I.p.b 帧 I帧:帧内编码帧 :尽可能去除图像空间冗余信息来压缩传输数据量的帧内编码图像:P帧:前向预测编码帧: 通过充分将低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像,也叫 ...
- 2021-01-29:redis同步机制是怎样的?
福哥答案2021-01-30: [答案1:](https://italk.mashibing.com/question/detail/ques_00006009)全量同步master服务器会开启一个后 ...
- 2021-03-20:给定一个二维数组matrix,其中的值不是0就是1,返回全部由1组成的子矩形数量。
2021-03-20:给定一个二维数组matrix,其中的值不是0就是1,返回全部由1组成的子矩形数量. 福大大 答案2021-03-20: 按行遍历二维数组,构造直方图. 单调栈,大压小.有代码. ...
- uni-app介绍
"优你"框架 uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/ ...
- Kubernetes 架构原则和对象设计
Kubernet¶ Kubernetes 架构原则和对象设计¶ 什么是云计算¶ 云计算平台的分类¶ 以Openstack为典型的虚拟化平台 虚拟机构建和业务代码部署分离. 可变的基础架构使后续维护风险 ...
- python学习之-------OS 文件夹和文件操作
# OS模块 :查看一个文件夹下所有文件,这个文件夹有文件夹,不能用walk# -- coding: UTF-8 --import osimport sys#C:\Users\Administrato ...
- Python连接es笔记三之es更新操作
本文首发于公众号:Hunter后端 原文链接:Python连接es笔记三之es更新操作 这一篇笔记介绍如何使用 Python 对数据进行更新操作. 对于 es 的更新的操作,不用到 Search() ...
- 由C# yield return引发的思考
前言 当我们编写 C# 代码时,经常需要处理大量的数据集合.在传统的方式中,我们往往需要先将整个数据集合加载到内存中,然后再进行操作.但是如果数据集合非常大,这种方式就会导致内存占用过高,甚至可能导致 ...
- RSA 加密解密
from Crypto.Util.number import bytes_to_long, long_to_bytes, getPrime import libnum # 一.取两个素数 p = ge ...
- P3498 [POI2010]KOR-Beads 题解
前言: 最近在做哈希的题,发现了这道好题,看题解里很多大佬的方法都很巧妙,自己就发一个较为朴素的方法吧. 题意: 题目传送门 给你一个序列,需要求出数 k,使划分的子串长度为 k 时,不同的子串数量最 ...