PyStringObject定义在stringobject.h中,附有不少注释说明。
JasonLee 2011.08.08 0:33
1. PyStringObject实际上是一组字符,以'\0'结尾,且因为有只是长度的变量,所以也可以包含'\0'作为内容。
2. PyStringObject是不可变对象,所以a = "hello"和a = "world"先后执行后,a指向的是不同的对象了。
3. PyStringObject保存了hash值避免重复计算。
4. 采用intern机制来处理相同的字符串对象。
结构体定义如下:
typedef struct {
PyObject_VAR_HEAD //不定长对象
long ob_shash; //如果还没计算,为-1
int ob_sstate; //如果该字符串在interned字典中,则该标志不为0。而且在这种情况下,两个来自interned字典的引用不算进ob_refcnt
char ob_sval[1]; //用来作为指针指向保存字符串的内存区域,包含ob_size+1个元素
} PyStringObject;
计算哈希值的算法有必要MARK下,以后可以参考使用。
static long
string_hash(PyStringObject *a)
{
register Py_ssize_t len;
register unsigned char *p;
register long x;
if (a->ob_shash != -1)
return a->ob_shash;
len = Py_SIZE(a);
p = (unsigned char *) a->ob_sval;
x = *p << 7;
while (--len >= 0)
x = (1000003*x) ^ *p++;
x ^= Py_SIZE(a);
if (x == -1)
x = -2;
a->ob_shash = x;
return x;
}
intern机制是用来确保相同的值的字符串对象只有一个存在,并且使得比较操作可以仅仅通过指针比较来完成。
它实际上是维护了一个字典(dict/map)interned,当字符串被interned时,会查找字典中键的存在,如果没有则放入。
这种方法可以在空间上节省,并不能节省时间,因为要判断是否存在于interned字典中,需要先创建字符串对象,才能去表中查找。
此外,这种方法默认针对Python的关键字、单字符、空串等。
当然,Python也提供了intern()内置函数来缓存用户想要的字符串对象。
缓存hash值以及intern机制为解释器加速了20%。
最后,是关于字符串的连接。
比如:str = str1 + str2 + str3 + str4,由于PyStringObject is immutable,所以会为3个+号执行3次内存的分配和复制
而如果将待连接的字符串放入可迭代对象中,使用string_join(PyStringObject的join操作),就会一次计算所需的总共内存大小,一次分配,然后再全部复制过去。
JasonLee 2011.08.08 0:33