【通过Cpython3.9源码看看python字符串的缓存机制】

news2025/1/21 15:32:14

在这里插入图片描述

基本说明

CPython中,字符串intern机制是一种字符串对象缓存机制,用于避免创建多个相同内容的字符串对象,以减少内存使用。具体来说,如果两个字符串对象的内容相同,那么这两个字符串对象实际上会共享同一块内存空间。

CPython使用了一个哈希表(即interned字典)来实现字符串对象缓存机制。当一个字符串对象被创建时,CPython会首先检查该字符串对象是否已经被缓存,如果已经被缓存,则直接返回缓存中的字符串对象的引用;否则,将该字符串对象添加到缓存中,并返回该字符串对象的引用。当一个字符串对象不再被使用时,CPython会从缓存中删除该字符串对象,以释放其占用的内存空间。

CPython中,字符串对象的intern操作可以使用sys.intern函数或PyUnicode_InternInPlace函数来实现。其中,sys.intern函数是Python层面的函数,它接受一个字符串作为参数,并返回该字符串在字符串缓存中的引用。PyUnicode_InternInPlace函数是C语言层面的函数,它接受一个字符串对象的指针作为参数,并将该字符串对象进行intern操作。

相关源码

void
PyUnicode_InternInPlace(PyObject **p)
{
    PyObject *s = *p;
#ifdef Py_DEBUG
    assert(s != NULL);
    assert(_PyUnicode_CHECK(s));
#else
    if (s == NULL || !PyUnicode_Check(s)) {
        return;
    }
#endif

    /* If it's a subclass, we don't really know what putting
       it in the interned dict might do. */
    if (!PyUnicode_CheckExact(s)) {
        return;
    }

    if (PyUnicode_CHECK_INTERNED(s)) {
        return;
    }

#ifdef INTERNED_STRINGS
    if (interned == NULL) {
        interned = PyDict_New();
        if (interned == NULL) {
            PyErr_Clear(); /* Don't leave an exception */
            return;
        }
    }

    PyObject *t;
    t = PyDict_SetDefault(interned, s, s);

    if (t == NULL) {
        PyErr_Clear();
        return;
    }

    if (t != s) {
        Py_INCREF(t);
        Py_SETREF(*p, t);
        return;
    }

    /* The two references in interned are not counted by refcnt.
       The deallocator will take care of this */
    Py_SET_REFCNT(s, Py_REFCNT(s) - 2);
    _PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;
#endif
}

源码解释

该函数是Python解释器内部使用的字符串intern机制的一部分。字符串intern机制通过维护一个内部的字符串缓存,以便在程序中出现多个相同内容的字符串时可以复用同一块内存空间,从而减少内存占用。

函数PyUnicode_InternInPlace的作用是将给定的字符串对象原地(in-place)进行intern操作,即将其添加到字符串缓存中。如果字符串对象已经被添加到字符串缓存中,则不会重复添加。

接下来,我们对函数的关键代码进行逐一解释:

PyObject *s = *p;

该语句从指针p中获取字符串对象的引用,并将其存储在s变量中。

#ifdef Py_DEBUG
    assert(s != NULL);
    assert(_PyUnicode_CHECK(s));
#else
    if (s == NULL || !PyUnicode_Check(s)) {
        return;
    }
#endif

这段代码是在进行一些前置检查,如果字符串对象NULL或者不是PyUnicodeObject类型,则直接返回。

if (!PyUnicode_CheckExact(s)) {
    return;
}

这段代码是检查字符串对象是否是PyUnicodeObject类型的精确子类型。如果不是,则直接返回。

if (PyUnicode_CHECK_INTERNED(s)) {
    return;
}

该语句检查字符串对象是否已经被添加到字符串缓存中,如果已经被添加,则直接返回。

if (interned == NULL) {
    interned = PyDict_New();
    if (interned == NULL) {
        PyErr_Clear(); /* Don't leave an exception */
        return;
    }
}

该语句创建了一个名为interned的全局变量,用于存储字符串缓存。如果interned变量为NULL,则创建一个新的空字典对象,并将其赋值给interned变量。如果创建字典对象失败,则清除异常状态,并返回。

PyObject *t;
t = PyDict_SetDefault(interned, s, s);

if (t == NULL) {
    PyErr_Clear();
    return;
}

这段代码是将字符串对象s添加到interned字典中,并获取interned字典中与s相同键值的对象。如果添加成功,则t变量将指向s,否则t变量为NULL。如果添加失败,则清除异常状态并返回。

if (t != s) {
    Py_INCREF(t);
    Py_SETREF(*p, t);
    return;
}

如果t变量不等于s,则说明字典中已经存在与s相同内容的字符串对象,直接使用t代替s。具体做法是将t的引用计数加1,将指针p所指的字符串对象指向t,然后返回。

/* The two references in interned are not counted by refcnt.
   The deallocator will take care of this */
Py_SET_REFCNT(s, Py_REFCNT(s) - 2);
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;

这段代码是将字符串对象s的引用计数减去2,并设置其为interned状态。由于在interned字典中,字符串对象s本身的引用计数没有被计入,因此需要将其引用计数减去2,以保证在字符串对象不再被使用时能够被正确地释放。同时,将字符串对象s的状态设置为SSTATE_INTERNED_MORTAL,表示该字符串对象在interned字典中被使用时是可变的。

演示说明

下面是一个简单的示例,用于演示如何使用PyUnicode_InternInPlace函数将字符串对象进行intern操作:

import sys

# 创建两个字符串对象
a = "hello"
b = "hello"

# 输出字符串对象的引用地址
print("a: ", hex(id(a)))
print("b: ", hex(id(b)))

# 将字符串对象进行 intern 操作
a = sys.intern(a)
b = sys.intern(b)

# 输出字符串对象的引用地址
print("a: ", hex(id(a)))
print("b: ", hex(id(b)))

该示例首先创建了两个相同内容的字符串对象ab,并输出它们的引用地址。然后,通过sys.getrefcount函数获取字符串对象的引用计数,并将其作为参数传递给PyUnicode_InternInPlace函数进行intern操作。最后,输出字符串对象的引用地址,可以发现ab的引用地址相同,说明它们指向了同一块内存空间,即字符串对象被成功地缓存了。

>>> # 创建两个字符串对象
>>> a = "hello"
>>> b = "hello"
>>>
>>> # 输出字符串对象的引用地址
>>> print("a: ", hex(id(a)))
a:  0x1e22d9824b0
>>> print("b: ", hex(id(b)))
b:  0x1e22d9824b0
>>>
>>> # 将字符串对象进行 intern 操作
>>> a = sys.intern(a)
>>> b = sys.intern(b)
>>>
>>> # 输出字符串对象的引用地址
>>> print("a: ", hex(id(a)))
a:  0x1e22d9824b0
>>> print("b: ", hex(id(b)))
b:  0x1e22d9824b0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vue基础】前端工程化Vue项目

一、创建Vue项目步骤 1、新建一个文件夹,起名为vue_project01 2、在该文件夹中打开cmd,输入指令vue ui,打开图形化界面 3、此时跳转到网页,根据以下步骤配置vue项目 1)项目名命名为test01;选择包管理器为…

使用R语言进行方差分析(ANOVA、ANCOVA)(一)

方差分析(一) 1. ANOVA模型拟合1.1 aov()函数1.2 表达式中各项的顺序 2. 单因素方差分析(One-Way ANOVA)2.1 绘制各组均值及其置信区间的图形2.2 多重比较-TukeyHSD2.2.1 成对比较图2.2.2 多重均值比较-TukeyHSD(更易理…

图的存储(邻接矩阵邻接表)

图的存储 文章目录 图的存储1 邻接矩阵1.1 邻接矩阵存储结构定义1.2 完整代码应用 2 邻接表2.1 邻接表存储结构定义2.2 完整代码应用 1 邻接矩阵 A [ i ] [ j ] 1 A[i][j]1 A[i][j]1 表示顶点i与顶点j邻接,即i与j之间存在边或者弧。 A [ i ] [ j ] 0 A[i][j]0 A…

使用jdk17 搭建Hadoop3.3.5和Spark3.3.2 on Yarn集群模式

搭建Hadoop3.3.5和Spark3.3.2 on Yarn集群模式,使用jdk17 搭建Hadoop3.3.5和Spark3.3.2 on Yarn集群模式1. 创建一台虚拟机2. 安装jdk17(1)下载jdk17(2)安装jdk17(3)配置环境变量 3. 虚拟机之间…

【音视频第15天】webRTC协议(2)

目录 协议ICESTUNNATTURNSDPSDP结构 Signaling and ConnectingSignaling: How peers find each other in WebRTCConnecting and NAT Traversal with STUN/TURN Signalingsdp协议WebRTC如何使用sdpWebRTC会话示例 Connecting为什么WebRTC需要一个专用的子系统来连接?现实世界的…

Day949.遗留系统之殇:为什么要对遗留系统进行现代化? -遗留系统现代化实战

遗留系统之殇:为什么要对遗留系统进行现代化? Hi,我是阿昌,今天学习记录是关于遗留系统之殇:为什么要对遗留系统进行现代化?的内容。 不知道你是否跟曾经一样,身处一个遗留系统的漩涡之中&…

数据结构-基数排序

基数排序是和其他的各类排序方式都不同的方式,之前的各类排序,如快速排序,堆排序,冒泡排序等等,都是通过关键字之间的比较和移动记录这两种操作来实现的,而基数排序不需要记录关键字之间的比较。所谓基数排…

ubuntu 安装vmware tool

1在虚拟机种站到安装vmware-tools 然后重启虚拟机 2在磁盘中可以看到如下文件,将zip文件移动到桌面解压备用 3关闭虚拟机 找到编辑虚拟机设置 4点击左侧 CD/dvd(SATA) 如果是使用镜像文件,改成使用物理驱动器. 5 打开命令行 cd 桌面 (如…

CSS基础——盒子模型的一些属性概念

目录 display visibility overflow 文档流 元素在文档流中的特点 块元素 内联元素 浮动 float 浮动元素特点 清除浮动 clear 小练习 效果图 具体实现 高度塌陷问题 BFC 特点 如何开启BFC 解决方案 本篇的最终练习 效果图如下: 具体实现 disp…

ChatGPT原理解释

写了一本介绍ChatGPT原理的课程 结构如下 01、介绍ChatGPT及其原理 1.1 ChatGPT的概述 1.2 什么是自然语言处理(NLP) 1.3 深度学习与NLP的关系 1.4 GPT模型的介绍 02、GPT原理探讨 2.1 GPT模型的输入与输出 2.2 GPT模型的结构 2.3 GPT模型的预训练方法…

flv怎么无损转换成mp4格式,3大超级方法分享

flv格式是目前在视频分享媒体播放网站上广泛使用的一种视频文件格式,可以在网站窗口中直接播放,这类视频文件还能够有效保护版权。但是有些时候我们可能需要将flv格式的视频转换为其他格式,比如mp4。但是该怎么操作呢? 其实有很多…

数据挖掘:汽车车交易价格预测(测评指标;EDA)

目录 一、前期工作 1.赛题介绍 赛题分析: 分类和回归问题的评价指标有如下一些形式: (下文2.1和2.2会用到) 2.数据简介 3.探索性分析-EDA介绍 二、实战演练 2.1分类指标评价计算示例 2.2回归指标评价计算示例 2.3数据探索…

抠图,扣掉背景图片

Remove Image Backgrounds, Free HD, No Signup - Pixian.AI https://pixian.ai/ 从电脑本地选取图片,然后会自动扣掉背景,点击DOWNLOAD就可以了 第一个:Pixian.AI 这是一款国外的在线抠图网站,把需要扣除背景的图片拖拽进来&am…

简单的了解下 Fetch API 的工作原理

简介 Fetch API是一种现代的Web API,提供了一种异步获取网络资源的方法。由于其简单性、灵活性和一致性,它已经成为Web应用程序中获取数据和资源的流行选择。在本文中,我们将深入探讨Fetch API的核心特性,并了解其工作原理。 Fetc…

Windows逆向安全(一)之基础知识(七)

汇编C语言类型转换 类型转换 类型转换的使用场景 类型转换一般为由数据宽度小的转换成数据宽度大的,不然可能会有高位数据被截断的现象,引起数据丢失 需要一个变量来存储一个数据,刚开始这个数据的数据宽度较小,后来发现存不下…

什么是UML?

文章目录 00 | 基础知识01 | 静态建模类图对象图用例图 02 | 动态建模时序图通信图状态图活动图 03 | 物理建模构件图部署图 UML(Unified Model Language),统一建模语言,是一种可以用来表现设计模式的直观的,有效的框图…

【图像分割】Grounded Segment Anything根据文字自动画框或分割环境配置和基本使用教程

1 环境配置 要求:python>3.8, pytorch>1.7, torchvision>0.8 官方地址:https://github.com/IDEA-Research/Grounded-Segment-AnythingMarrying Grounding DINO with Segment Anything & Stable Diffusion & BLIP & Whisper &am…

命运坎坷的保定县

保定的词语分解 保护而安定之。诗小雅天保:“天保定尔,亦孔之固。”谓稳固地保有。 晋 陆云 大将军宴会被命作诗:“皇皇帝祜,诞隆骏命。四祖正家,天禄保定。” 保定府?保定县? 今天提起“保定…

将“每日造型”变成长久习惯,戴森Airwrap™美发棒为何成为最好的“美丽投资”?

做头发、换发型是一个大工程,这几乎成了一种固定印象。虽然卷发棒已成为几乎“人手必备”的头发造型工具,但使用起来往往“现实很骨感”,不是使用频次极低,就是被束之高阁,每天都自己做头发换造型,只能是一…

陶泓达:4.19黄金原油早间精准策略!

黄金方面: 周二(4月18日)美市早盘,现货黄金一度跌至1991.05美元低点,但随后迅速拉升近20美元,向上触及2010.71美元高点,短线走出V型反转行情。本周,美国经济日历上没有太多关键数据会影响黄金和美元。因此&…