https://m.toutiao.com/is/JEmDrHQ/

英文:https://arpitbhayani.me/blogs/string-interning

作者:arpit

译者:豌豆花下猫(“Python猫”公众号作者)

声明:本翻译是出于交流学习的目的,基于 CC BY-NC-SA 4.0 授权协议。为便于阅读,内容略有改动。

每种编程语言为了表现出色,并且实现卓越的性能,都需要有大量编译器级与解释器级的优化。

由于字符串是任何编程语言中不可或缺的一个部分,因此,如果有快速操作字符串的能力,就可以迅速地提高整体的性能。

在本文中,我们将深入研究 Python 的内部实现,并了解 Python 如何使用一种名为字符串驻留(String Interning)的技术,实现解释器的高性能。 本文的目的不仅在于介绍 Python 的内部知识,而且还旨在使读者能够轻松地浏览 Python 的源代码;因此,本文中将有很多出自 CPython 的代码片段。

全文提纲如下:

(在 Python猫 公众号回复数字“0215”,下载高清思维导图)

1、什么是“字符串驻留”?

字符串驻留是一种编译器/解释器的优化方法,它通过

缓存一般性的字符串,从而节省字符串处理任务的空间和时间。

这种优化方法不会每次都创建一个新的字符串副本,而是仅为每个适当的不可变值保留一个字符串副本,并使用指针引用之。

每个字符串的唯一拷贝被称为它的

intern,并因此而得名 String Interning。Python猫注:String Interning 一般被译为“字符串驻留”或“字符串留用”,在某些语言中可能习惯用 String Pool(字符串常量池)的概念,其实是对同一种机制的不同表述。intern 作为名词时,是“实习生、实习医生”的意思,在此可以理解成“驻留物、驻留值”。

查找字符串 intern 的方法可能作为公开接口公开,也可能不公开。现代编程语言如 Java、Python、PHP、Ruby、Julia 等等,都支持字符串驻留,以使其编译器和解释器做到高性能。

2、为什么要驻留字符串?

字符串驻留提升了字符串比较的速度。 如果没有驻留,当我们要比较两个字符串是否相等时,它的时间复杂度将上升到 O(n),即需要检查两个字符串中的每个字符,才能判断出它们是否相等。

但是,如果字符串是固定的,由于相同的字符串将使用同一个对象引用,因此只需检查指针是否相同,就足以判断出两个字符串是否相等,不必再逐一检查每个字符。由于这是一个非常普遍的操作,因此,它被典型地实现为指针相等性校验,仅使用一条完全没有内存引用的机器指令。

字符串驻留减少了内存占用。 Python 避免内存中充斥多余的字符串对象,通过

享元设计模式共享和重用已经定义的对象,从而优化内存占用。

3、Python的字符串驻留

像大多数其它现代编程语言一样,Python 也使用字符串驻留来提高性能。在 Python 中,我们可以使用

is运算符,检查两个对象是否引用了同一个内存对象。

因此,如果两个字符串对象引用了相同的内存对象,则

is运算符将得出

True,否则为

False。

>>>

"python"

is

"python"

True

我们可以使用这个特定的运算符,来判断哪些字符串是被驻留的。在 CPython 的,字符串驻留是通过以下函数实现的,声明在 unicodeobject.h 中,定义在 unicodeobject.c 中。PyAPI_FUNC(void)

PyUnicode_InternInPlace(PyObject**);

为了检查一个字符串是否被驻留,CPython 实现了一个名为

PyUnicode_CHECK_INTERNED的宏,同样是定义在 unicodeobject.h 中。

这个宏表明了 Python 在

PyASCIIObject结构中维护着一个名为

interned的成员变量,它的值表示相应的字符串是否被驻留。

#define

PyUnicode_CHECK_INTERNED(op)\(((PyASCIIObject*)(op))->state.interned)

4、字符串驻留的原理

在 CPython 中,字符串的引用被一个名为

interned的 Python 字典所存储、访问和管理。 该字典在第一次调用字符串驻留时,被延迟地初始化,并持有全部已驻留字符串对象的引用。

4.1 如何驻留字符串?

负责驻留字符串的核心函数是

PyUnicode_InternInPlace,它定义在 unicodeobject.c 中,当调用时,它会创建一个准备容纳所有驻留的字符串的字典

interned,然后登记入参中的对象,令其键和值都使用相同的对象引用。

以下函数片段显示了 Python 实现字符串驻留的过程。voidPyUnicode_InternInPlace(PyObject**p){PyObject*s=*p;.........

//LazilybuildthedictionarytoholdinternedStringsif(interned==NULL){interned=PyDict_New();if(interned==NULL){PyErr_Clear();return;}}PyObject*t;//Makeanentrytotheinterneddictionaryforthe//givenobjectt=PyDict_SetDefault(interned,s,s);.........//Thetworeferencesininterneddict(keyandvalue)are//notcountedbyrefcnt.//unicode_dealloc()and_PyUnicode_ClearInterned()take//careofthis.Py_SET_REFCNT(s,Py_REFCNT(s)-2);//SetthestateofthestringtobeINTERNED_PyUnicode_STATE(s).interned=SSTATE_INTERNED_MORTAL;}

4.2 如何清理驻留的字符串?

清理函数从

interned字典中遍历所有的字符串,调整这些对象的引用计数,并把它们标记为

NOT_INTERNED,使其被垃圾回收。一旦所有的字符串都被标记为

NOT_INTERNED,则

interned字典会被清空并删除。

这个清理函数就是

_PyUnicode_ClearInterned,在 unicodeobject.c 中定义。

void_PyUnicode_ClearInterned(PyThreadState*tstate){.........

//GetallthekeystotheinterneddictionaryPyObject*keys=PyDict_Keys(interned);.........//InternedUnicodestringsarenotforciblydeallocated;//rather,wegivethemtheirstolenreferencesback//andthenclearandDECREFtheinterneddict.for(Py_ssize_ti=0;i

5、字符串驻留的实现

既然了解了字符串驻留及清理的内部原理,我们就可以找出 Python 中所有会被驻留的字符串。

为了做到这点,我们要做的就是在 CPython 源代码中查找

PyUnicode_InternInPlace 函数的调用,并查看其附近的代码。下面是在 Python 中关于字符串驻留的一些有趣的发现。

5.1 变量、常量与函数名

CPython 对常量(例如函数名、变量名、字符串字面量等)执行字符串驻留。

以下代码出自codeobject.c,它表明在创建新的

PyCode对象时,解释器将对所有编译期的常量、名称和字面量进行驻留。PyCodeObject*

PyCode_NewWithPosOnlyArgs

(

intargcount,

intposonlyargcount,

intkwonlyargcount,

intnlocals,

intstacksize,

intflags,PyObject*code,PyObject*consts,PyObject*names,PyObject*varnames,PyObject*freevars,PyObject*cellvars,PyObject*filename,PyObject*name,

intfirstlineno,PyObject*linetable){........

if(intern_strings(names)<

0){

return

NULL;}

if(intern_strings(varnames)<

0){

return

NULL;}

if(intern_strings(freevars)<

0){

return

NULL;}

if(intern_strings(cellvars)<

0){

return

NULL;}

if(intern_string_constants(consts,

NULL)<

0){

return

NULL;}........}

5.2 字典的键

CPython 还会驻留任何字典对象的字符串键。

当在字典中插入元素时,解释器会对该元素的键作字符串驻留。以下代码出自 dictobject.c,展示了实际的行为。

有趣的地方:在

PyUnicode_InternInPlace函数被调用处有一条注释,它问道,我们是否真的需要对所有字典中的全部键进行驻留?

intPyDict_SetItemString(PyObject*v,

const

char*key,PyObject*item){PyObject*kv;

interr;kv=PyUnicode_FromString(key);

if(kv==

NULL)

return

-1;

//InvokingStringInterningonthekeyPyUnicode_InternInPlace(&kv);/*XXXShouldwereally?*/err=PyDict_SetItem(v,kv,item);Py_DECREF(kv);returnerr;}

5.3 任何对象的属性

Python 中对象的属性可以通过

setattr函数显式地设置,也可以作为类成员的一部分而隐式地设置,或者在其数据类型中预定义。

CPython 会驻留所有这些属性名,以便实现快速查找。 以下是函数

PyObject_SetAttr的代码片段,该函数定义在文件object.c中,负责为 Python 对象设置新属性。intPyObject_SetAttr(PyObject*v,PyObject*name,PyObject*value){........

PyUnicode_InternInPlace(&name);........}

5.4 显式地驻留

Python 还支持通过

sys模块中的

intern函数进行显式地字符串驻留。

当使用任何字符串对象调用此函数时,该字符串对象将被驻留。以下是 sysmodule.c 文件的代码片段,它展示了在

sys_intern_impl函数中的字符串驻留过程。

staticPyObject*

sys_intern_impl

(PyObject*

module,PyObject*s){........

if(PyUnicode_CheckExact(s)){Py_INCREF(s);PyUnicode_InternInPlace(&s);

returns;}........}

6、字符串驻留的其它发现

只有编译期的字符串会被驻留。 在解释时或编译时指定的字符串会被驻留,而动态创建的字符串则不会。

julia语言 python解释器_深入Python解释器源码,我终于搞明白了字符串驻留的原理...相关推荐

  1. 深入 Python 解释器源码,我终于搞明白了字符串驻留的原理!

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 作者:arpit 译者:豌豆花下猫 声明:本翻译是出于交流学习的目 ...

  2. python创建一个字典、关键字为只包含字母的字符串_探究Python源码,终于弄懂了字符串驻留技术...

    摘要:在本文中,我们将深入研究 Python 的内部实现,并了解 Python 如何使用一种名为字符串驻留(String Interning)的技术,实现解释器的高性能. 每种编程语言为了表现出色,并 ...

  3. 探究Python源码,终于弄懂了字符串驻留技术

    摘要:在本文中,我们将深入研究 Python 的内部实现,并了解 Python 如何使用一种名为字符串驻留(String Interning)的技术,实现解释器的高性能. 每种编程语言为了表现出色,并 ...

  4. 第一章 第一节:Python基础_认识Python

    Python基础入门(全套保姆级教程) 第一章 第一节:Python基础_认识Python 1. 什么是编程 通俗易懂,编程就是用代码编写程序,编写程序有很多种办法,像c语言,javaPython语言 ...

  5. Python语言打造智能语音助手——附完整源码

    Python语言打造智能语音助手--附完整源码 随着智能家居.智能办公等领域的逐渐兴起,实现语音控制与交互已成为了一种趋势.而Python语言是一门极具魅力的编程语言,其强大的库.简洁的语法以及易于学 ...

  6. python 语义网络_从Python代码到APP,你只需要一个小工具:GitHub已超3000星

    机器学习开发者想要打造一款 App 有多难?事实上,你只需要会 Python 代码就可以了,剩下的工作都可以交给一个工具.近日,Streamlit 联合创始人 Adrien Treuille 撰文介绍 ...

  7. python求素数积_用Python求素数的快速算法源码示例

    本篇文章为Python算法相关,用Python求素数的快速算法源码示例.算法在Python的学习中算是一个要点,能研究明白算法的同学都可以算的上是Python的大牛了. 首先简单的来说下什么是素数:质 ...

  8. Python concurrent.future 使用教程及源码初剖

    前言 原文发在这里的 Python concurrent.future 使用教程及源码初剖 垃圾话 很久没写博客了,想了想不能再划水,于是给自己定了一个目标,写点 concurrent.future ...

  9. 教你用python制作人脸卡通画(附源码)

    教你用python制作人脸卡通画(附源码) 效果展示: 让我们开始学习之路: 原理:利用第三方人脸接口将图像人脸化 第三方接口注册地址:https://ai.minivision.cn/#/login ...

  10. 计算机毕业设计Python+djang公务员考试信息管理系统(源码+系统+mysql数据库+Lw文档)

    项目介绍 随着社会的发展,近些年来的毕业生大多数会选择考研,考公员和考试教师资格证以增加自己在未来社会中的竞争能力.为了让这些有志之士能够有一个更好的学习和交流平台,我们通过计Python+djang ...

最新文章

  1. Tomcat异常退出
  2. 行将消失的烧瓦匠人:“仿古”热潮下迎新生曙光
  3. android 中间按钮突出,Android 实现 按钮从两边移到中间动画效果
  4. [k8s]elk架构设计-k8s集群里搭建
  5. 5、图书类别查询功能
  6. QToolButton设置背景无效的思考
  7. android最大json,Android:解析大型JSON文件
  8. Mysql 主从复制常用管理任务介绍
  9. 全国大学生数学建模2014年A题嫦娥三号软着陆轨道设计与控制策略论文与代码
  10. 「第五篇」全国电子设计竞赛-电源题设计方案总结
  11. http服务器异步响应,python – 具有异步响应的Twisted http服务器,其中请求必须等待数据变为可用或超时...
  12. Java GregorianCalendar getTimeZone()方法与示例
  13. C++设计模式-使用Qt框架模拟策略模式(Strategy)+简单工厂实现商场促销
  14. 【Java从0到架构师】Spring - 复杂对象、Converter
  15. ubuntu ifconfig_VirtualBox中ubuntu的LAMP项目(温度采集)
  16. ios手机游戏 带你体验拉斯维加斯的疯狂
  17. 英语“就近原则”和“就远原则”
  18. hello语音为什连接不上服务器,Hello语音交友怎么玩_Hello语音交友打不开
  19. 许知远:嗨!Julia
  20. bzoj 2959: 长跑(LCT+并查集)

热门文章

  1. 无人机项目跟踪记录七十七----蓝牙模块详解
  2. Dubbo视频教程《基于Dubbo的分布式系统架构视频教程》----课程列表
  3. Mac 安装 CentOS镜像 及 VMware虚拟机
  4. 用ps绘制中国银行的标志
  5. 接口测试搭建之JMeter接口测试与SoapUI接口测试
  6. APP社交类源代码分享直接上车
  7. 《王道》数据结构笔记整理2022
  8. Proteus7.8 ISIS制图8个LED(针对初次使用者)
  9. 神经网络预测python_bp神经网络预测python
  10. 炫酷而不复杂,RDP报表实用又方便