现在的位置: 首页 > 综合 > 正文

Python源码剖析[8] —— 字符串对象(3)

2013年10月10日 ⁄ 综合 ⁄ 共 1925字 ⁄ 字号 评论关闭

[绝对原创 转载请注明出处]

Python源码剖析

——字符串对象PyStringObject(3)

本文作者: Robert Chen(search.pythoner@gmail.com )

5.      PyStringObject效率相关问题

关于PyStringObject,有一个地球人都知道的严重影响Python程序执行效率的问题,有一种说法,绝大部分执行效率特别低下的Python程序都是由于没有注意到这个问题所致。下面我们就来看看这个在Python中举足轻重的问题——字符串连接。

假如现在有两个字符串“Python”和“Ruby”,在JavaC#中,都可以通过使用“+”操作符,将两个字符串连接在一起,得到一个新的字符串“PythonRuby”。当然,Python中同样提供了利用“+”操作符连接字符串的功能,然而不幸的是,这样的做法正是万恶之源。

Python中通过“+”进行字符串连接的方法效率及其低下,其根源在于Python中的PyStringObject对象是一个不可变对象。这就意味着当进行字符串连接时,实际上是必须要创建一个新的PyStringObject对象。这样,如果要连接NPyStringObject对象,那么就必须进行N-1次的内存申请及内存搬运的工作。这将严重影响Python的执行效率。

官方推荐的做法是通过利用PyStringObject对象的join操作来对存储在listtuple中的一组PyStringObject对象进行连接操作,这种做法只需要分配一次内存,执行效率将大大提高。

下面我们通过考察源码来更细致地了解这一问题。

通过“+”操作符对字符串进行连接时,会调用string_concat函数:

[stringobject.c]

static PyObject* string_concat(register PyStringObject *a, register PyObject *bb)

{

    register unsigned int size;

    register PyStringObject *op;

#define b ((PyStringObject *)bb)

    ……

    size = a->ob_size + b->ob_size;

    /* Inline PyObject_NewVar */

    op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size);

    if (op == NULL)

        return PyErr_NoMemory();

    PyObject_INIT_VAR(op, &PyString_Type, size);

    op->ob_shash = -1;

    op->ob_sstate = SSTATE_NOT_INTERNED;

    memcpy(op->ob_sval, a->ob_sval, (int) a->ob_size);

    memcpy(op->ob_sval + a->ob_size, b->ob_sval, (int) b->ob_size);

    op->ob_sval[size] = '/0';

    return (PyObject *) op;

#undef b

}

   

对于任意两个PyStringObject对象的连接,就会进行一次内存申请的动作。而如果利用PyStringObject对象的join操作,则会进行如下的动作(假设是对list中的PyStringObject对象进行连接):

[stringobject.c]

static PyObject* string_join(PyStringObject *self, PyObject *orig)

{

    char *sep = PyString_AS_STRING(self);

    const int seplen = PyString_GET_SIZE(self);

    PyObject *res = NULL;

    char *p;

    int seqlen = 0;

    size_t sz = 0;

    int i;

    PyObject *seq, *item;

。。。。。。//获得listPyStringObject对象的个数,保存在seqlen

 

    for (i = 0; i < seqlen; i++) 

{

        const size_t old_sz = sz;

        item = PySequence_Fast_GET_ITEM(seq, i);

        sz += PyString_GET_SIZE(item);

        if (i != 0)

            sz += seplen;

    }

/* 申请内存空间

抱歉!评论已关闭.