You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中字符串+=操作为何未出现二次复杂度?实验解惑

关于Python字符串+=操作的性能疑惑解答

你的原有认知确实是错误的——Python早已对字符串的+=操作做了针对性优化,并非每次都会复制整个字符串内容。

核心原因

Python从2.2版本开始,当字符串变量是当前作用域内的唯一引用时,解释器会直接在原字符串的内存空间末尾追加内容,而不是创建新字符串并复制原有内容。这种优化让res += "a"的均摊时间复杂度变为O(1),整个foo函数的时间复杂度就是O(n),所以执行时间会随iterations线性增长。

为什么foo2只快两倍

foo2使用列表append后join的方式,是一次性分配足够内存再完成所有字符串的拼接,理论上是最高效的字符串拼接方式。但foo的优化已经把复杂度降到了和foo2同级的线性,两者的差距仅来自于常数级的执行开销(比如列表操作的轻量性 vs 字符串扩展的底层操作),所以出现两倍左右的性能差是正常的。

验证方法

1. 跟踪字符串的内存地址(id())

修改foo函数,打印每次+=后的内存地址:

def foo(i):
    res = ""
    print(f"初始id: {id(res)}")
    for _ in range(5):
        res += "a"
        print(f"追加后id: {id(res)}")
    return res

foo(5)

如果输出的id始终不变,说明是在原对象上原地扩展,没有创建新字符串。

2. 查看引用计数(sys.getrefcount())

通过引用计数确认res是唯一引用:

import sys

def foo(i):
    res = ""
    for _ in range(3):
        print(f"当前引用数: {sys.getrefcount(res)}")
        res += "a"
    return res

foo(3)

输出的引用数通常是2(sys.getrefcount本身会增加一个临时引用),说明没有其他变量持有res的引用,解释器可以安全地进行原地扩展。

3. 破坏唯一引用,观察性能退化

如果给res增加额外引用,+=就会退化为全量复制:

import timeit

def foo(i):
    res = ""
    temp = res  # 增加一个额外引用
    for _ in range(i):
        res += "a"  
    return res

def foo2(i):
    res = []
    for _ in range(i):
        res.append("a")
    return "".join(res)

iterations = 10000
print(timeit.timeit('foo(iterations)', globals=globals(), number=100))
print(timeit.timeit('foo2(iterations)', globals=globals(), number=100))

此时foo的执行时间会远大于foo2,呈现明显的二次增长趋势,验证了没有优化时的性能表现。

内容的提问来源于stack exchange,提问作者kosciej16

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:00:05