有什么理由不使用“+”来连接两个字符串?[重复]

2025-04-16 08:57:00
admin
原创
17
摘要:问题描述:Python 中一个常见的反模式是使用循环连接一系列字符串+。这很糟糕,因为 Python 解释器必须为每次迭代创建一个新的字符串对象,最终会花费平方级的时间。(最新版本的 CPython 在某些情况下似乎可以优化这一点,但其他实现却无法做到,因此不鼓励程序员依赖这种方法。)''.join这才是正确...

问题描述:

Python 中一个常见的反模式是使用循环连接一系列字符串+。这很糟糕,因为 Python 解释器必须为每次迭代创建一个新的字符串对象,最终会花费平方级的时间。(最新版本的 CPython 在某些情况下似乎可以优化这一点,但其他实现却无法做到,因此不鼓励程序员依赖这种方法。)''.join这才是正确的做法。

然而,我听过有人说(包括在 Stack Overflow 上),永远不要使用+来进行字符串连接,而应该始终使用''.join或 来作为格式字符串。我不明白为什么在只连接两个字符串的情况下会这样。如果我的理解正确,它应该不会花费平方级的时间,而且我认为a + b它比''.join((a, b))或更简洁、更易读'%s%s' % (a, b)

连接两个字符串是好习惯吗+?还是有什么我没注意到的问题?


解决方案 1:

用 连接两个字符串并没有错+。事实上,它比 更容易阅读''.join([a, b])

不过,你说得对,用 连接两个以上的字符串+是一个 O(n^2) 的操作(相比于 的 O(n) join),因此效率很低。但这与使用循环无关。即使a + b + c + ...是 O(n^2),原因在于每次连接都会产生一个新的字符串。

joinCPython2.4 及更高版本尝试缓解这种情况,但在连接 2 个以上的字符串时仍然建议使用。

解决方案 2:

加法运算符是连接两个Python 字符串的完美解决方案。但如果你不断添加两个以上的字符串(n > 25),你可能需要考虑其他方法。

''.join([a, b, c])trick 是一种性能优化。

解决方案 3:

认为永远不要使用 + 进行字符串连接,而应该始终使用 ''.join 的假设可能只是个误区。的确,使用 ++会创建不可变字符串对象的不必要临时副本,但另一个鲜为人知的事实是,join循环调用通常会增加 的开销function call。让我们以你的例子为例。

创建两个列表,一个来自链接的 SO 问题,另一个来自更大的虚构

>>> myl1 = ['A','B','C','D','E','F']
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)]

让我们创建两个函数,UseJoinUsePlus使用各自的join功能+

>>> def UsePlus():
    return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)]

>>> def UseJoin():
    [''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)]

让我们用第一个列表运行 timeit

>>> myl=myl1
>>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus")
>>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
2.48 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
2.61 usec/pass
>>> 

它们的运行时间几乎相同。

让我们使用 cProfile

>>> myl=myl2
>>> cProfile.run("UsePlus()")
         5 function calls in 0.001 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    0.001    0.001 <pyshell#1376>:1(UsePlus)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


>>> cProfile.run("UseJoin()")
         5005 function calls in 0.029 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.015    0.015    0.029    0.029 <pyshell#1388>:1(UseJoin)
        1    0.000    0.000    0.029    0.029 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     5000    0.014    0.000    0.014    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {range}

而且看起来使用 Join 会导致不必要的函数调用,从而增加开销。

现在回到问题本身。是否应该在所有情况下都不鼓励使用+over join

我认为不是,应该考虑一些事情

  1. 所讨论的字符串的长度

  2. 连接操作数。

当然,在开发过程中过早的优化是有害的。

解决方案 4:

当多人协作时,有时很难确切地知道发生了什么。使用格式字符串而不是字符串连接可以避免一个我们经常遇到的麻烦:

假设一个函数需要一个参数,并且你编写它期望得到一个字符串:

In [1]: def foo(zeta):
   ...:     print 'bar: ' + zeta

In [2]: foo('bang')
bar: bang

所以,这个函数在整个代码中可能会被频繁使用。你的同事可能确切地知道它的作用,但不一定完全了解其内部原理,甚至可能不知道该函数需要传入一个字符串。所以他们最终可能会得到这样的结果:

In [3]: foo(23)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

/home/izkata/<ipython console> in foo(zeta)

TypeError: cannot concatenate 'str' and 'int' objects

如果您只使用格式字符串,则不会有问题:

In [1]: def foo(zeta):
   ...:     print 'bar: %s' % zeta
   ...:     
   ...:     

In [2]: foo('bang')
bar: bang

In [3]: foo(23)
bar: 23

对于定义的所有类型的对象也是如此__str__,它们也可以传入:

In [1]: from datetime import date

In [2]: zeta = date(2012, 4, 15)

In [3]: print 'bar: ' + zeta
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

TypeError: cannot concatenate 'str' and 'datetime.date' objects

In [4]: print 'bar: %s' % zeta
bar: 2012-04-15

所以是的:如果您可以使用格式字符串,那就这样做,并利用 Python 提供的功能。

解决方案 5:

根据 Python 文档,使用 str.join() 可以在各种 Python 实现中保持性能一致。虽然 CPython 优化了 s = s + t 的二次行为,但其他 Python 实现可能不会这样做。

CPython 实现细节:如果 s 和 t 均为字符串,某些 Python 实现(例如 CPython)通常可以对形式为 s = s + t 或 s += t 的赋值执行就地优化。在适用的情况下,此优化可以显著降低运行时间平方的可能性。此优化与版本和实现相关。对于性能敏感的代码,建议使用 str.join() 方法,该方法可确保在不同版本和实现之间保持一致的线性连接性能。

Python 文档中的序列类型(参见脚注[6])

解决方案 6:

我做了一个快速测试:

import sys

str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx
"

for i in range(int(sys.argv[1])):
    str = str + e

并计时:

mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  8000000
8000000 times

real    0m2.165s
user    0m1.620s
sys     0m0.540s
mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  16000000
16000000 times

real    0m4.360s
user    0m3.480s
sys     0m0.870s

显然,针对这种情况有一个优化a = a + b。它并没有像人们猜测的那样表现出 O(n^2) 的时间复杂度。

所以至少从性能方面来说,使用+是可以的。

解决方案 7:

我在 Python 3.8 中使用以下内容

string4 = f'{string1}{string2}{string3}'

解决方案 8:

''.join([a, b])是比+更好的解决方案。

因为代码的编写方式不应该对 Python 的其他实现(PyPy、Jython、IronPython、Cython、Psyco 等)造成不利影响

形式 a += b 或 a = a + b 即使在 CPython 中也是脆弱的,并且在不使用 引用计数的实现中根本不存在 (引用计数是一种存储引用、指针或资源句柄数量的技术,例如对象、内存块、磁盘空间或其他资源

https://www.python.org/dev/peps/pep-0008/#programming-recommendations

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用