python源码剖析----第三章(上)

时间:2022-06-28 18:38:12

字符串对象是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所需内存也包含字符数组内的元素所需的额外内存