PythonTip >> 博文 >> python

python源码剖析笔记---PyStingObject对象

liushulinle 2014-11-29 15:11:51 点击: 51087 | 收藏


1.PyStringObject 与 PyString_Type
PyStringObject是对字符串对象的实现,是Python中的可变长度内存的对象,因此继承自PyVarObject。
PyStringObject的定义如下:
typedef struct{
PyObject_VAR_HEAD
long ob_shash; //字符串的hash值,未计算前初始为-1,该字段防止每次使用都要重复计算hash值,提高效率
int ob_sstate;//标记该对象是否经过intern机制处理
char ob_sval[1];//指向字符串在内存中的内存首地址,实际上保存了字符串的第一个字符,具体看下面的例子
} PyStringObject.
其中,PyObject_VAR_HEAD宏定义了PyVarObject的成员(ob_refcnt, ob_type, ob_size)
注意,ob_sval指向的内存大小为ob_size + 1个字节,最后一个多出的位置为'\0'标识字符串结尾。需要说明的是,和C语言不同,PyStringObject的字符串长度又ob_size指定,因此字符串内部可以有'\0'。

PyTypeObject PyString_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
...,
sizeof(char).   //指定每个元素的大小为一个char的大小
....
&string_as_number,     //指定tp_as_number的操作
&string_as_sequence,   //指定tp_as_sequence的操作
&string_as_mapping,   //指定tp_as_mapping的操作
(hashfunc)string_hash,  //指定hash函数
...

};
从PyString_Type的定义中可以看到,指定了tp_as_number, tp_as_sequence和tp_as_mapping操作,因此字符串支持数值、序列和映射操作。

2.Python创建PyStringObject对象
Python有两种方式创建PyStringObject对象,一种直接从C的字符串创建,另一种附加了一个参数size,表示截取前size个字符创建。这里只剖析第一种创建方式,第二种基本没有太大变化。
PyObject * PyString_FromString(const char* str)
{
    register size_t size;
    register PyStringObject *op;
    size = strlen(str);

    //判断字符串长度
    if(size > PY_SSIZE_MAX)
    {
        return NULL; //如果字符串的长度超过python预定义的最大长度,则不创建
    }

    //处理null string
    if(size == 0 && (op = nullstring) != null)
    {
        return (PyObject*) op;//如果为空串,且空串对象之前已经创建过,则直接返回空串对象
    }

    //处理字符
   if(size == 1 && (op = characters[*str & UCHAR_MAX]) != null)  return (PyObject*) op;

   //创建新的PyStringObject对象,并初始化
    op = (PyStringObject*) PyObject_MALLOC(sizeof(PyStringObject) + size);
    PyObject_INIT_VAR(op, &PyString_Type, size); //这里,实际上对ob_type和ob_size进行赋值
    op -> ob_shash = -1;
    op -> ob_sstate = SSTATE_NOT_INTERNED;
    memcpy(op -> ob_sval, str, size + 1);
    ...
    return (PyObject*) op;
}
如我们利用“Python"创建PyStringObject对象,内存状态如下:
ob_refcnt
ob_type
ob_size
ob_shash
ob_sstate
ob_sval






ref
type
6
-1
0
P
y
t
h
o
n
\0
其中,灰色部分为PyStringObject的内存,白色部分为额外内存,注意,ob_sval实际上存储了字符串的第一个字符。

3.字符串对象的intern机制
PyStringObject对象的intern机制目的是:对于被intern之后的字符串,比如”Python“,在整个Python的运行期间,系统中都只有唯一的一个PyStringObject对象与字符串”Python“对应。
这样做有两个好处: 1. 节省空间   2.当比较两个PyStringObject对象是否相同时,如果它们都被intern了(可以通过ob_sstate判断),那么只需要简单检查它们对应的是不是同一个PyStringObject对象即可
需要注意的是,该机制并不能省去创建PystringObject对象的时间开销,因为在创建字符串对象时,是先创建临时对象,再判断临时对象在intern机制对应的表里是否存在,存在则返回表里对应的对象,否则返回新创建的对象。

4.字符缓冲池
和小整数缓冲池类似,python为一个字节的PyStringObject对象设计了一个缓冲池characters[UCHAR_MAX], 其中UCHAR_MAX默认被宏定义为0xff.

5.PyStringObject效率相关问题
Python中的字符串‘+’操作的效率是非常低下的,这是因为PyStringObject是不可变对象,每次进行叠加操作,都要重新申请内存,拷贝数据。
如果有大量的字符串需要拼接,首先应考虑字符串的join(list)操作,该操作一次性统计list中串的总长度,然后只会申请一次内存,拷贝所有数据进行填充,当拼接的串较多时,效率提升明显。


作者:liushulinle | 分类: python | 标签: python Python源码 | 阅读: 51087 | 发布于: 2014-11-29 15时 |