Python源码剖析----第三章(下)

来源:互联网 发布:美国经济数据公布时间表 编辑:程序博客网 时间:2024/06/05 22:57

PyStingObject对象的intern机制

根据之前讨论的PyStringObject对象创建的方式,假设创建一个String对象a,其表示的字符串是“Python”, 随后若再一次为字符串"Python“创建一个String对象,通常情况下,Python会重新申请一个内存来创建一个新的PyStringObject对象b,a与b是完全不同的两个对象,各占一段内存,尽管其内部维护的字符数组是完全相同的,那以此类推,若在程序中需要创建100个"Python"字符串对象则必然会浪费大量内存。

因此Python为PyStringObject对象引入了intern机制,目的就是对被intern之后的字符串,在整个python的运行期间,系统中都只有唯一的一个与字符串对应的PyStringObject对象,以上一个”Python“为例,如果对于对象a应用了intern机制,那在其之后创建b时,Python会在系统中记录的已经被intern机制处理了的PyStringObject对象中查找,如果发现该字符数组对应的PyStringObject对象已经存在了,最终会返回该对象的引用,从而可以节省空间,这样也使得当需要判断两个被intern的PyStringObject对象是否相同时,只需要简单地检查它们对应的PyObject* 是否相同即可。
这个Intern机制不仅节省了内存空间,也简化了对PyStringObject对象的比较。不论是PyString_FrmString还是PyString_FromStringAndSize,当首次创建的yStringObject对象字符数组的长度为0或1时,都会进行intern机制的处理:
[stringobject.c]PyObject * PyString_FromString(const char *str){register size_t size;register PyStringObject *op;.....//创建PyStringObject对象;// intern长度较短的PyStringObject对象if(size == 0) {PyObject *t = (PyObject *)op;PyString_InternInPlace(&t);op = (PyStringObject *)t;nullstring = op;} else if (size == 1) {PyObject *t = (PyObject *)op;PyString_InternInPlace(&t);op = (PyStringObject *)t;characters[*str & UCHAR_MAX] = op;}return (PyObject *) op;}
由此看出,PyString_InternInPlace正是负责完成对一个对象进行intern操作的函数。
[stringobject.c]void PyString_InternInPlace(PyObject **p){register PyStringObject *s = (PyStringObject *)(*p);PyObject *t;//对PyStringObject进行类型和状态检查if (!PyString_CheckExact(s))return;if (PyString_CHECK_INTERNED(s))return;//创建记录经intern机制处理后的PyStringObject的dictif (interned == NULL) {interned = PyDict_New();}//检查对象是否存在对应的intern后的PyStringObject对象t = PyDict_GetItem(interned, (PyObject *)s);if (t) {Py_INCREF(t);Py_DECREF(*p);*p = t;return;}//在interned中记录检查PyStringObject对象, 调整计数及intern状态标志PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s);s->ob_refcnt -=2;PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAl;}
PyString_InternInPlace会进行一系列操作,包括:
1)检查传入的对象是否是一个PyStringObject对象,intern机制只能应用在PyStirng_Object对象上,甚至对于它的派生类对象系统都不会应用intern机制;
2)检查传入的PyStringObject对象是否已经被intern机制处理过了,Python不会对同一个PyStringObject对象进行一次以上的intern操作;
3)检查PyStringObject对象是否存在对应的interned中, 若存在则调整引用计数并返回;
4)若不存在,则在interned中添加对应PyStringObject对象记录,并调整对象引用计数及intern状态标志;


其中,所提到的interned是intern机制的关键,在Python中interned其实是一个PyDictObject对象,是记录着被intern机制处理过的PyStringObject对象映射关系的(key,value)集合.当对一个PyStringObject对象a应用intern机制时,首先会在interned这个dict中检查是否有维护与a相同原生字符串的PyStringObject对象b,如果确实存在对象b,那么指向a的PyObject指针就会指向b,而a的引用计数减1,如若interned中还不存在这样的b,那么就将a记录到interned中。
可以看出,Python并不是在创建PyStringObject对象时就通过interned实现了节省空间的目的,事实上,无论如何,一个合法的PyStringObject对象都会被创建,intern机制是在对象被创建之后才起作用,intern机制会减少这个临时变量的引用计数,当计数减为0时对象会被回收。需要注意的是,对于被intern机制处理了的PyStringObject对象,Python采用了特殊的引用计数,Python的设计者规定在interned中的对象指针不能视为a对象的有效引用,因为如果是有效引用的话,那么PyStringObject对象a的计数在Python结束之前永远都不可能为0.
0 0