Python 中 string += 'a' 这种写法效率很低吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Alpacino
V2EX    问与答

Python 中 string += 'a' 这种写法效率很低吗?

  •  1
     
  •   Alpacino 2020-07-30 13:04:21 +08:00 5545 次点击
    这是一个创建于 1981 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在别的论坛上看见:“你 python for loop 还 string += “a”,这一下子就暴露不科班了。” 这个说法。

    42 条回复    2020-08-01 10:16:43 +08:00
    gwy15
        1
    gwy15  
       2020-07-30 13:08:25 +08:00
    py str 不可变,如果要拼接字符串,类似 java 的 sb,用 List[str] 再 join 。
    sagaxu
        2
    sagaxu  
       2020-07-30 13:09:48 +08:00 via Android
    随便什么语言这么写都慢
    Procumbens
        3
    Procumbens  
       2020-07-30 13:10:16 +08:00
    @sagaxu C++不慢
    sagaxu
        4
    sagaxu  
       2020-07-30 13:12:10 +08:00 via Android
    @Procumbens C++也慢,这种写法不能提前分配好长度合适的内存
    realityone
        5
    realityone  
       2020-07-30 13:14:41 +08:00
    Hstar
        6
    Hstar  
       2020-07-30 13:19:38 +08:00
    是 for loop 结合 string += 'a' 在 python 中慢
    假设目前 string 为 "aa"内存地址是 a1,在一次+=之后会生成一个新的字符串对象 "aaa" 存在 a2 并将 string 指向 a2
    所以 for loop 套 += 字符串操作会产生 N 个字符串对象的创建操作,效率较慢
    deorth
        7
    deorth  
       2020-07-30 13:23:55 +08:00
    所以说怎样写才快
    binux
        8
    binux  
       2020-07-30 13:29:59 +08:00 via Android
    @sagaxu #2 js 在 v8 下不慢
    STRRL
        9
    STRRL  
       2020-07-30 13:32:10 +08:00 via Android   7
    反对这种写个 += 就暴露什么科班不科班。。
    过早优化乃万恶之源
    JeffGe
        10
    JeffGe  
       2020-07-30 13:37:24 +08:00 via Android
    @realityone 我看这里面写的是 Python 2
    CallMeReznov
        11
    CallMeReznov  
       2020-07-30 13:43:50 +08:00
    所以说怎样写才快?
    heygum97
        12
    heygum97  
       2020-07-30 13:49:09 +08:00 via iPhone
    我寻思搜索引擎是不好用吗 这种问题
    djFFFFF
        13
    djFFFFF  
       2020-07-30 13:53:09 +08:00
    这不一定是说效率低吧,python 语法糖比较多,for loop 三四行的代码很多时候 list comprehension 一行代码就搞定了
    binbinyouliiii
        14
    binbinyouliiii  
       2020-07-30 13:53:13 +08:00 via Android
    正式项目不能这么写,自己为了 debug 随便你
    marcong95
        15
    marcong95  
       2020-07-30 13:53:38 +08:00
    所以所谓的科班就要先 malloc 再 strcat 么(手动滑稽)

    我寻思着 string.join 也不怎么科班吧?可能我上课没听课(++手动滑稽)
    love
        16
    love  
       2020-07-30 13:56:41 +08:00   1
    python 本身就效率很低,就不用追求这么细了吧
    Vegetable
        17
    Vegetable  
       2020-07-30 14:09:23 +08:00
    https://gist.github.com/luliangce/f33d175e5a53db83ee2d2af91d3711be

    这和循环的规模有很明显的关系。
    单个循环次数比较小时,直接拼接效果更好,循环次数达到一定量级之后使用列表才有意义,这个量级我测试大概在 1000 左右,而且持续提高规模并不会明显提高差距。
    楼上给的测试链接看起来,并不是很具有代表意义。
    Trim21
        18
    Trim21  
       2020-07-30 14:12:08 +08:00
    正经项目拼接字符串都是用 StringIO 的吗
    iyaozhen
        19
    iyaozhen  
       2020-07-30 14:43:59 +08:00
    2.6 好像就有优化过了,你可以试试,实际不慢(或者没那么夸张)
    该用还是用,一般循环次数也是有限,肯定不是性能问题的大头,甚至末尾都算不上
    misaka19000
        20
    misaka19000  
       2020-07-30 14:53:08 +08:00
    反编译看一下字节码不就知道了
    yangyaofei
        21
    yangyaofei  
       2020-07-30 14:58:11 +08:00
    都用 Python 了,还在乎那点性能损失么...
    真的在乎性能就不会用 python 吧,或者多线程多进程比这个性能提升多多了.
    JCZ2MkKb5S8ZX9pq
        22
    JCZ2MkKb5S8ZX9pq  
       2020-07-30 14:58:19 +08:00
    python cook book 好像有提过这个
    但以我平时写的那些小破代码来说,可读性和直观程度更加重要一些。优不优化区别不大,真搞到需要优化了,我应该也能有预算找人帮我整个重构了。
    goodryb
        23
    goodryb  
       2020-07-30 15:12:04 +08:00
    所以比较牛逼的写法应该是什么样子的,都用 python 了不是更应该注重开发效率吗
    changwei
        24
    changwei  
       2020-07-30 15:54:24 +08:00
    我很好奇编译器能不能自动识别出这种大量+=的语句,然后底层自动转换成 stringBuffer 来拼接?就像 c 或者 java 的 for 循环里面可以把 i++优化成++i 一样,我觉得这种比较有规律并且很普遍的写法在编译器上可以考虑一下特殊优化了。
    a132811
        25
    a132811  
       2020-07-30 17:06:35 +08:00
    几乎没有差别, 时间都是随着 n 线性增长

    ```python
    >>> timeit.timeit('for i in range(int(1e8)):s+="a"', setup='s=""', number=1)
    11.196388629000012

    >> timeit.timeit('for i in range(int(1e8)):s.write("s")', setup='from io import StringIO; s=StringIO()', number=1)
    8.931465667999987

    >>> timeit.timeit('"".join(["a" for i in range(int(1e8))])', setup='from io import StringIO; s=StringIO()', number=1)
    4.056428481000012
    ```
    MinQ
        26
    MinQ  
       2020-07-30 17:09:35 +08:00   1
    纠结这个能省几秒啊,不应该是写得差不多了再来查哪里是性能瓶颈然后针对优化么
    wittyfans
        27
    wittyfans  
       2020-07-30 17:19:19 +08:00 via iPhone
    在 stack overflow 上也看到了这个问题,可以看看。

    https://stackoverflow.com/questions/39675898/is-python-string-concatenation-bad-practice

    不科班的说法只能说明这人有点自以为是。
    lbp0200
        28
    lbp0200  
       2020-07-30 17:21:51 +08:00
    想要性能,首先抛弃 Python,改用 C
    caviar /td>
        29
    caviar  
       2020-07-30 17:30:32 +08:00
    个人感觉说这话的人也不是特别的科班。
    确实 python 的 str 是 immutable 的,所以理论上 for loop 中 使用 string += "a" 的复杂度非常恐怖,尤其是在 string 很长的情况下,毕竟每次都要完整复制一份。
    但是实际上因为这个 case 太常见了,CPython 很早就有这个优化。即,如果这个 string 没有别的 reference ( non-alised )的时候,会直接 destructive update (官方的叫法是 in-place string concatenation )。因此从某种角度上说,python 的 str 可能会是 mutable 的,类似 list,每次创建时增长一下预留的空间。详细的说明可以看 wtf-python 里的 let's make a giant string
    https://github.com/satwikkansal/wtfpython#-lets-make-a-giant-string

    当然如果场景合适或数量非常大甚至是瓶颈的话,还是用 join 之类的为好。
    caviar
        30
    caviar  
       2020-07-30 17:41:14 +08:00
    呃... wtf-python 我是很早之前读的,好像记错了,那一段并没有详细讲这件事....
    cyspy
        31
    cyspy  
       2020-07-30 17:48:50 +08:00 via Android
    难道 for +=比 join 易懂吗?
    nutting
        32
    nutting  
       2020-07-30 18:05:54 +08:00
    一条语句没什么吧,循环里才值得商榷
    acumen
        33
    acumen  
       2020-07-30 19:22:26 +08:00
    任何抛开场景的讨论都是耍流氓(手动狗头
    classyk
        34
    classyk  
       2020-07-30 19:24:38 +08:00
    @sagaxu 当然可以,注意 reserve 函数的使用。
    AlohaV2
        35
    AlohaV2  
       2020-07-30 19:28:35 +08:00
    @sagaxu C++有 small string optimization,前面字串不太大的话应该还行
    AlohaV2
        36
    AlohaV2  
       2020-07-30 19:30:47 +08:00
    科班不科班都可以这样写,这么写可读性挺好的。
    要不要这样写取决于你这行代码在整个程序里运行几遍(换句话说性能分析出来占多少的执行时间比例)。
    matrix1010
        37
    matrix1010  
       2020-07-30 22:09:18 +08:00
    前几天 twitter 上刚看到有人讨论过

    https://gist.github.com/llllllllll/7ad5905275233f1fb3868f4a67793616
    Alpacino
        38
    Alpacino  
    OP
       2020-07-31 02:30:51 +08:00
    所以如果要避免的话,就用''.join() ?
    Acoolda
        39
    Acoolda  
       2020-07-31 10:03:35 +08:00 via Android
    别听那些沙雕文章忽悠,实用易懂是关键。
    julyclyde
        40
    julyclyde  
       2020-07-31 16:42:01 +08:00
    如果效率有问题那是语言自己没做好自己的工作
    zxCoder
        41
    zxCoder  
       2020-07-31 20:00:16 +08:00
    所以应该怎么写呢?上面这么多回复好像都没提到过
    Alpacino
        42
    Alpacino  
    OP
       2020-08-01 10:16:43 +08:00
    @zxCoder 同问。。好像是用''.join()
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     945 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 18:17 PVG 02:17 LAX 10:17 JFK 13:17
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86