为什么/如何CPython reverse()这么快? CPython的reverse()是否会重新指向列表中的指针?

时间:2019-08-22 17:29:42

标签: python python-3.x memory-management reverse cpython

假设我想编写一个不使用split函数即可反转句子中单词的函数。这是一种可能的解决方案:

def reverse_words_1(s):
    i, n, r = 0, len(s), []
    while i < n:
        while i < n and s[i] == ' ': i += 1
        if i == n: break
        p = i 
        while i < n and s[i] != ' ': i += 1
        # Instead of appending here and then reversing after the while
        # loop is done, we could r.insert(0, ..). But insert is much
        # slower than append/reverse because insert always requires that
        # each pointer in the list must be copied and moved. Whereas
        # append only requires copying if there isn't enough space for
        # the new element in the currently allocated memory block.
        # Helpful explanations:
        # https://stackoverflow.com/questions/7776938/python-insert-vs-append
        # https://bytes.com/topic/python/answers/34036-timing-difference-insert-vs-append-reverse
        r.append( s[p : i] )
    r.reverse()
    return ' '.join(r)

我的代码中的注释指出insertappend/reverse慢得多。但是我的评论实际上仅比较了insertappend采取的行动。我的评论未涉及reverse的动作或速度。

那么reverse在CPython中如何工作?我的猜测是reverse正在重新指向列表中的指针。像这样:

def reverse(lst):
    l, r = 0, len(lst) - 1
    while l < r:
        lst[l], lst[r] = lst[r], lst[l]
        l += 1
        r -= 1

这是CPython在内部执行reverse函数的大致方式吗?

如果我对reverse的工作方式的猜测是正确的,那么我想重新指向指针比复制和移动指针要快得多?

1 个答案:

答案 0 :(得分:1)

或多或少是listobject.c中代码的工作方式。下面的代码反转切片,但是reverse方法使用整个列表调用该切片。

/* Reverse a slice of a list in place, from lo up to (exclusive) hi. */
static void
reverse_slice(PyObject **lo, PyObject **hi)
{
    assert(lo && hi);

    --hi;
    while (lo < hi) {
        PyObject *t = *lo;
        *lo = *hi;
        *hi = t;
        ++lo;
        --hi;
    }
}