CPython中字符串乘法是如何实现的?

7 投票
2 回答
1887 浏览
提问于 2025-04-18 12:58

在Python中,可以用整数来乘字符串:

>>> 'hello' * 5
'hellohellohellohellohello'

这在CPython中是怎么实现的呢?

我特别希望能找到源代码的相关信息;这个Mercurial代码库对我来说实在是太复杂了,我搞不定。

2 个回答

6

对于Python 3.x,具体的实现可以在这个链接找到:Objects/unicodeobject.c。特别是,从12175行开始,定义了一个叫unicode_repeat的函数:

static PyObject*
unicode_repeat(PyObject *str, Py_ssize_t len)
{
    PyObject *u;
    Py_ssize_t nchars, n;

    if (len < 1)
        _Py_RETURN_UNICODE_EMPTY();

    /* no repeat, return original string */
    if (len == 1)
        return unicode_result_unchanged(str);

    if (PyUnicode_READY(str) == -1)
        return NULL;

    if (PyUnicode_GET_LENGTH(str) > PY_SSIZE_T_MAX / len) {
        PyErr_SetString(PyExc_OverflowError,
                        "repeated string is too long");
        return NULL;
    }
    nchars = len * PyUnicode_GET_LENGTH(str);

    u = PyUnicode_New(nchars, PyUnicode_MAX_CHAR_VALUE(str));
    if (!u)
        return NULL;
    assert(PyUnicode_KIND(u) == PyUnicode_KIND(str));

    if (PyUnicode_GET_LENGTH(str) == 1) {
        const int kind = PyUnicode_KIND(str);
        const Py_UCS4 fill_char = PyUnicode_READ(kind, PyUnicode_DATA(str), 0);
        if (kind == PyUnicode_1BYTE_KIND) {
            void *to = PyUnicode_DATA(u);
            memset(to, (unsigned char)fill_char, len);
        }
        else if (kind == PyUnicode_2BYTE_KIND) {
            Py_UCS2 *ucs2 = PyUnicode_2BYTE_DATA(u);
            for (n = 0; n < len; ++n)
                ucs2[n] = fill_char;
        } else {
            Py_UCS4 *ucs4 = PyUnicode_4BYTE_DATA(u);
            assert(kind == PyUnicode_4BYTE_KIND);
            for (n = 0; n < len; ++n)
                ucs4[n] = fill_char;
        }
    }
    else {
        /* number of characters copied this far */
        Py_ssize_t done = PyUnicode_GET_LENGTH(str);
        const Py_ssize_t char_size = PyUnicode_KIND(str);
        char *to = (char *) PyUnicode_DATA(u);
        Py_MEMCPY(to, PyUnicode_DATA(str),
                  PyUnicode_GET_LENGTH(str) * char_size);
        while (done < nchars) {
            n = (done <= nchars-done) ? done : nchars-done;
            Py_MEMCPY(to + (done * char_size), to, n * char_size);
            done += n;
        }
    }

    assert(_PyUnicode_CheckConsistency(u, 1));
    return u;
}

然后,在13703行,这个函数被用作PyUnicode对象的一个功能,叫做sq_repeat,它是PySequenceMethods的一部分。

5

注意:我将以Python 3为例来回答,在Python 3中,字符串类型叫做PyUnicode。Python 2也差不多。

当执行BINARY_MULTIPLY这个操作码时(在Python/ceval.c文件里),有两个可能的地方会被调用:PyNumberMethods.nb_multiplyPySequenceMethods.sq_repeat。这个过程在PyNumber_Multiply中,位于Objects/abstract.c文件里:

PyObject *
PyNumber_Multiply(PyObject *v, PyObject *w)
{
    PyObject *result = binary_op1(v, w, NB_SLOT(nb_multiply));
    if (result == Py_NotImplemented) {
        // call sq_repeat on either side if available

PyUnicode实现了后者,具体在unicode_repeat这个地方:

static PyObject*
unicode_repeat(PyObject *str, Py_ssize_t len)
{
    // ...

撰写回答