字符串对象是Python变长对象中的不可变对象,对象的长度在对象创建时才能被确定,但对象创建之后其所维护的数据就不能再改变。对于PyStringObject的定义如下:
[stringobject.h] typedef struct{ PyObject_VAR_HEAD long ob_shash; int ob_sstate; char ob_sval[1];/*作为字符指针指向对象所维护的实际字符串所在内存*/ } PyStringObject;
由于字符串的长度由PyObject_VAR_HEAD中的ob_size字段来维护,因此PyStringObject所维护的字符串对象中间可能出现字符‘\0',但是同C中的字符串一样,PyStringObject内部维护的字符串必须以‘\0'结尾,因此ob_sval指向的是一段包含ob_size+1个元素的内存,且ob_sval[ob_size]=‘\0'。
PyStringObject对应的类型对象PyString_Type定义如下
[stringobject.c] PyTypeObject PyString_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "str", sizeof(PyStringObject), sizeof(char), /*tp_itemsize, 变长对象所保存的元素的单位长度*/ ... (reprfunc)string_repr, &string_as_number, /*tp_as_number, 字符串对象支持数值操作*/ &string_as_sequence, /*tp_as_sequence, 支持序列操作*/ ... string_new, /*tp_new*/ PyObject_Del, /*tp_free*/ }; 字符缓冲池 字符串对象体系中的字符缓冲池是以静态变量的形式存在着,不同于小整数的缓冲池是在Python初始化时被创建的,字符缓冲池中的所有PyStringObject指针都为空。
[stringobject.c] PyObject* PyString_FromString(const char *str) { register size_t size; register PyStringObject *op; size = strlen(str); if (size > PY_SSIZE_T_MAX){ return NULL; } if (size==0 && (op=nullstring)!=NULL){ retrun (PyObject *)op; if(size==1 && (op = characters[*str & UCHAR_MAX]) != NULL){ return (PyObject *) op; op=(PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject)+size); ... memcpy(op->ob_sval, str, size+1); ... return (PyObject *) op; }
以PyString_FromString为例,要创建一个字符串对象,首先传入的参数必须是一个指向以NULL('\0')结尾的字符串的指针,先检查对应字符数组的长度,若长度大于定义的最大值,Python将不会创建对应的字符串对象,然后根据字符数组的长度大致分为三种情况,一种是传入的字符串是一个空串,对于空串,Python对于首次的请求会创建一个空字符串的PyStringObject对象,并将这个对象通过intern机制进行共享,然后将nullstring指向这个被共享的对象,以便在第二次创建空字符串的请求时,直接返回nullstring的引用即可,不再创建新的对象;当字符数组长度等于1时,首先判断缓冲池中是否已存在该字符对象,若有,则直接返回;最后一种情况是字符串不为空且长度大于1或者长度为1但不在字符对象的缓冲池中,则需要申请相应的内存空间(size=sizeof(PyStringObject)+sizeof(str)),这个空间不仅包含PyStringObject所需内存也包含字符数组内的元素所需的额外内存