1.字符串对象
PyStringObject是一个拥有可变长度内存的对象,对于表示‘hi’和'Python'的两个不同的PyStringObject对象,其内部所需的保存字符串内容的的内存空间显然是不一样的。同时,PyStringObject对象又是一个不变对象,当创建了一个PyStringObject对象之后,改对象内部维护的字符串就不能改变了。
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate;
char ob_sval[1];
}PyStringObject
PyStringObject的头部实际是一个PyObject_VAR_HEAD,其中有一个ob_size变量保存着对象中维护的可变长度内存的大小。虽然在定义中ob_sval是一个字符的字符数组,但是ob_sval实际上是作为一个字符指针指向一段内存的,这段内存保持着这个字符串对象所维护的实际字符串。显然这段内存不会只是一个字节,这段内存的实际长度(字节)正是由ob_size来维护的,这个机制是Python中所有变长对象的实现机制。
同C中的字符串一样,PyStringObject内部维护的字符串在末尾必须以‘\0’结尾,但是由于字符串的实际长度是由ob_size维护的,所以PyStringObject表示的字符串对象中间是有可能出现‘\0'的。所以实际上,ob_sval指向的是一段长度为ob_size+1个字节的内存,而且必须满足ob_sval[ob_size]=='\0'
PyStringObject中的ob_shash变量的作用是缓存该对象的hash值,这样可以避免每一次都重新计算该字符串对象的hash值。如果一个PyStringObeject没有被计算过hash值,那么ob_shash的初始值-1。这个hash在剖析dict的时候会发挥巨大作用。生成hash的代码如下:
static long string_hash(PyStringObject *a)
(
register int len;
register unsigned char *p;
register long x;
if (a->ob_shash != -1 )
return a->ob_shash;
len = a->ob_size
p = (unsigned char *) a->ob_val
x = *p << 7;
while (--len >=0)
x=(1000003*x) ^ *p++;
x ^= a->ob_size;
if ( x==-1)
x= -2
a->ob_shash = x;
return a
)
ob_sstate变量标记了该对象是否经过intern机制的处理,关于PyStringObject的Intern机制,后面会详细介绍。
2.PyStringObject 所对应的类型对象 PyString_Type
3.PyStringObject对象的创建
4.字符串对象的intern机制
当字符数组的长度为0,或1时,需要进行一个特别的动作:PyString_InternInplace,这就是前面所提到的intern机制。
//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;
对于被intern之后的字符串,比如’Ruby',在整个Python运行期间,系统中只有唯一与字符串‘Ruby'对应的PyStringObject对象。
例如 a='Python' , b='Python' ,如果对于a应用了intern机制,那么之后要创建bd的时候,Python会首先在系统中记录的已经被intern机制处理了的PyStringObject对象中查找,如果发现该字符数组对应的PyStringObject对象已经存在,那么就将该对象的引用返回。而不是重新创建一个。下面是 PyString_InternInplace的源码:
首先会进行一系列的检查,检查是否PyStringObject对象,intern只能用于PyStringObject对象,甚至他的派生类对象都不会运用。第二检查是否已经被intern过,Python不会对同一个对象intern两次。
intern机制的核心是interned,interned实际上是一个PyDictObject对象,也就是我们常用的dict。现在一切都清楚了,所谓的intern机制的关键,就是系统中有一个(key,value)映射关系的集合,记录着被intern机制处理过的PyStringObject对象。后来作者又提到,其实PyStringObjectb并不是在创建的时候就通过interned实现了节省空间的目的,无论如何一个合法的PyStringObject对象总会被创建,尽管interned中已经有一个与之对应的PyStringObject对象了,而python在运行时创建了一个PyStringObject对象temp之后,基本上都会调用PyString_InternInplace对temp进行处理,interned会减少temp的引用计数,当引用计数为0时,就会被销毁,它只是作为一个临时对象昙花一现的在内存中闪现。