您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

探索Python源代码,最后了解字符串驻留技术。 [影视源码]

  • 时间:2022-06-24 00:17 编辑: 来源: 阅读:275
  • 扫一扫,手机访问
摘要:探索Python源代码,最后了解字符串驻留技术。 [影视源码]
在本文中,我们将深入研究Python的内部实现,了解Python是如何使用一种叫做字符串实习的技术来实现解释器的高性能的。 每一种编程语言都需要大量的编译器级和解释器级的优化,才能出类拔萃,达到卓越的性能。 因为字符串是任何编程语言不可或缺的一部分,如果你有快速操作字符串的能力,就可以快速提升整体性能。 在本文中,我们将深入研究Python的内部实现,了解Python是如何使用一种叫做String Interning的技术来实现解释器的高性能的。 本文的目的不仅是详细介绍Python的内部知识,也是为了让读者能够方便地浏览Python的源代码。因此,本文中会有很多来自CPython的代码片段。 全文如下:1 .什么是「弦驻」?字符串驻留是编译器/解释器的一种优化方法,通过缓存一般字符串来节省字符串求解任务的空间和时间。 这种优化方法并不是每次都创建一个新的字符串副本,只是为每个合适的不可变值保留一个字符串副本,并用指针引用它。 每个字符串的唯一副本称为其实习生,因此命名为字符串实习生。 字符串实习一般翻译为“字符串常驻”或“字符串保留”。在某些语言中,可能习惯使用字符串池的概念,这实际上是同一机制的不同表达。 internen做名词的时候是“实习生,实习生”的意思,可以理解为“常驻,常驻值” 查找string intern的方法可能会也可能不会作为公共接口公开。 现代编程语言,如Java、Python、PHP、Ruby、Julia等。,都支持字符串驻留以使它们的编译器和解释器达到高性能。 2.为什么要驻留一个字符串?字符串驻留提高了字符串比较的速度。 如果没有驻留,当我们要比较两个字符串是否相等时,它的时间复杂度会上升到O(n),即我们需要检查两个字符串中的每个字符来确定它们是否相等。 但是如果字符串是固定的,由于同一个字符串会被同一个对象引用,所以只要检查指针是否相同就足以判断两个字符串是否相等,不需要逐个检查每个字符。 因为这是一个非常常见的操作,所以它通常被实现为指针相等检查,只使用一条机器指令,根本没有内存引用。 字符串驻留减少了内存使用。 Python的内存避免被冗余的字符串对象填满,定义的对象通过元设计模式共享重用,从而优化内存占用。 3.Python中的字符串驻留与大多数其他现代编程语言一样,Python也使用字符串驻留来提高性能。 在Python中,我们可以使用is操作符来检查两个对象是否可以引用同一个内存对象。 因此,如果两个字符串对象引用同一个内存对象,is运算符将返回True,否则将返回False。 & gt& gt& gt“Python”是“python”真,我们可以使用这个特定的操作符来确定哪些字符串是常驻的。 在CPython中,字符串持久性是通过以下函数实现的,这些函数在unicodeobject.h中声明,在unicode object.c中定义。 PyAPI _ FUNC(void)py unicode _ interinplace(py object * *);为了检查一个字符串是否可以常驻,CPython实现了一个名为pyunicode _ check _ interconnected的宏,这个宏也是在unicodeobject.h中定义的 这个宏表示Python在PyASCIIObject结构中维护了一个名为interconnected的成员变量,其值表示对应的字符串是否可以常驻。 # define py unicode _ CHECK _ INTERNED(op)\(((PyASCIIObject *)(op))-& gt;State.interned)4。字符串驻留原理在CPython中,字符串的引用由一个名为interned的Python字典进行存储、访问和管理。 当第一次调用string resident时,字典延迟初始化,并保存所有常驻字符串对象的引用。 4.1如何存放一个字符串?负责常驻字符串的核心函数是PyUnicode _ InternInPlace,它是在Unicode Object中定义的。当被调用时,它将创建一个保存所有常驻字符串的字典interned,然后注册参与的对象,以便它们的键和值使用相同的对象引用。 下面的函数片段展示了Python对字符串驻留的实现。 void py unicode _ interinplace(py object * * p){ py object * s = * p;.........//如果(interned = = NULL){ interned = py dict _ New(),则延迟构建字典以保存被拘留的字符串;if(interned = = NULL){ PyErr _ Clear();返回;} } p object * t;//在interned字典中为//给定对象t = PyDict_SetDefault(interned,s,s)创建一个条目;.........//ref CNT不会对interned dict中的两个引用(键和值)进行计数。// unicode_dealloc()和_PyUnicode_ClearInterned()负责//这一点。Py_SET_REFCNT(s,Py _ ref CNT(s)-2);//将字符串的状态设置为INTERNED _PyUnicode_STATE(s)。interned = SSTATE _ INTERNED _凡人;}4.2如何清除常驻字符串?clearing函数遍历互连字典中的所有字符串,调整这些对象的引用计数,并将它们标记为not _ interconnected,以便对它们进行垃圾收集。 一旦所有字符串都被标记为not _ interconnected,互联字典将被清除和删除。 清除函数是_PyUnicode_ClearInterned,在unicodeobject.c中定义 void _ py unicode _ clear interned(PyThreadState * tstate){.........//获取实习字典PyObject *keys = PyDict_Keys的所有键(实习);.........//不会强制释放保留的Unicode字符串;//相反,我们把偷来的参考资料还给他们//然后清除并废除互联网上的字典。for(Py _ ssize _ t I = 0;我& ltn;i++){ py object * s = PyList _ GET _ ITEM(keys,I);.........switch(Py unicode _ CHECK _ INTERNED(s)){ case SSTATE _ INTERNED _不朽:Py_SET_REFCNT(s,Py _ ref CNT(s)+1);打破;case SSTATE _ INTERNED _ moral://还原被PyUnicode _ InternInPlace()忽略的//两个引用(键和值)。Py_SET_REFCNT(s,Py _ ref CNT(s)+2);打破;case SSTATE _ NOT _ INTERNED:/* fall through */default:Py _ UNREACHABLE();} //将字符串标记为NOT_INTERNED _PyUnicode_STATE。interned = SSTATE _ NOT _ INTERNED} //减少对initialized和// access keys对象的引用。py _ DECREF(keys);//清除字典PyDict _ Clear(interned);//清除对象interned Py _ CLEAR(interned);}5.字符串驻留的实现既然了解了字符串驻留和清理的内部原理,就可以找出Python中将要驻留的所有字符串。 要做到这一点,我们要做的就是在CPython源代码中寻找PyUnicode _ InternInPlace函数的调用,并查看其周围的代码。 这里有一些关于Python中字符串驻留的有趣发现。 5.1变量、常量和函数名CPython执行字符串驻留在常量上(如函数名、变量名、字符串文字等。) 下面的代码来自codeobject.c,它表明当创建一个新的PyCode对象时,解释器将驻留所有编译时常量、名称和文字。 PyCodeObject * PyCode _ NewWithPosOnlyArgs(int argcount,int posonlyargcount,int kwonlyargcount,int nlocals,int stacksize,int flags,PyObject *code,PyObject *consts,PyObject *names,PyObject *freevars,PyObject *cellvars,PyObject *filename,PyObject *name,int firstlineno,PyObject *linetable) {........if(intern _ string(names)& lt;0) {返回NULL} if(intern _ strings(varnames)& lt;0) {返回NULL} if(intern _ strings(freevars)& lt;0) {返回NULL} if(intern _ strings(cellvars)& lt;0) {返回NULL} if(intern _ string _ constants(consts,NULL)& lt;0) {返回NULL} ........} 5.2字典的key CPython也会托管任何字典对象的string键。 当一个元素被插入到字典中时,解释器将为该元素的键建立字符串驻留。 下面的代码来自dictobject.c并显示了实际的行为。 有趣的一点:在调用PyUnicode _ InternInPlace函数的地方有一个评论,它问道,我们真的需要在所有字典中驻留统一键吗?int PyDict _ SetItemString(py object * v,const char *key,py object * item){ py object * kv;int errkv = py unicode _ from string(key);if (kv == NULL)返回-1;//在键py unicode _ InternInPlace(& amp;kv);/* XXX真的要吗?*/ err = PyDict_SetItem(v,kv,item);py _ DECREF(kv);返回err} 5.3 Python中任意对象的属性,对象的属性可以通过setattr函数显式设置,也可以隐式设置为类成员的一部分,或者在其数据类型中保留。 CPython将托管所有这些属性名称,以便快速查找。 下面是函数PyObject_SetAttr的代码片段,该函数在文件object.c中定义,负责为Python对象设置新的属性。 int PyObject_SetAttr(PyObject *v,PyObject *name,PyObject *value) {........py unicode _ intern inplace(& amp;姓名);........}5.4显式驻留Python也支持通过sys模块中的intern函数显式驻留字符串。 当用任何字符串对象调用这个函数时,字符串对象将是常驻的。 以下是sysmodule.c文件中的代码片段,它显示了sys_intern_impl函数中的字符串驻留进程。 静态py object * sys _ intern _ impl(py object * module,PyObject *s) {........if(Py unicode _ check exact(s)){ Py _ INCREF(s);py unicode _ intern inplace(& amp;s);返回s;} .......} 6.字符串驻留的其他发现只有编译后的字符串才会驻留。 解释或编译期间指定的字符串将是常驻的,而动态创建的字符串不是。 Python的注:这个规律值得思考。我踩过一次...有两个知识点,我相信99%的人都不知道:字符串的join()方法是动态创建一个字符串,所以创建的字符串不会是常驻的;常数的折叠机制也发生在编译时,所以有时很容易和字符串驻留混淆。 推荐阅读《join()方法的妙用和实习生机制的弱点》。包含ASCII字符和下划线的字符串将是常驻的。 在编译期间,当字符串的文字是常驻的时,CPython确保只有匹配正则表达式[a-zA-Z0-9_]*的常数才是常驻的,因为它们非常接近Python标识符。 注意:关于Python中标识符的命名规则,Python 2版中只有“字母、数字和下划线”,但Python 3 . x版已经支持Unicode编码。 这部分推荐读《醒来吧!Python已经支持中文变量名了!参考资料字符串驻留(https://en.wikipedia.org/wiki/String _ Interning)CPYTHON优化(https://stummjr.org/posts/cpython-optimizations/)Python对象第三部分:字符串驻留(https://Medium.com/@ bdov _/https-medium-com-bdov-Python-objects-Part-III-String-interlocking-625 d3c 7319 de)Python字符串驻留的内部原理(http://guilload.com/ Python-String-Interning/)Python优化机制:常量折叠的妙用(https://mp.weixin.qq.com/s/p1zb _ linflwwplnya 5 ui 1 q)join()方法和Interning机制的弱点(https://mp.weixin.qq.com/s/ m2 uhvv,参考作者:Python猫


  • 全部评论(0)
资讯详情页最新发布上方横幅

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部