有什么理由不使用'+'连接两个字符串?

Tay*_*mon 117 python anti-patterns string-concatenation

Python中常见的反模式是+在循环中连接一系列字符串.这很糟糕,因为Python解释器必须为每次迭代创建一个新的字符串对象,并最终获得二次时间.(在某些情况下,CPython的最新版本显然可以优化它,但是其他实现不能,因此不鼓励程序员依赖它.)''.join是正确的方法.

不过,我听人说(这里包括对堆栈溢出),你应该永远不会使用+字符串连接,而是始终使用''.join或格式字符串.我不明白为什么如果你只是连接两个字符串就是这种情况.如果我的理解是正确的,它不应该花费二次时间,我认为a + b比任何一个''.join((a, b))或更清晰,更可读'%s%s' % (a, b).

+用于连接两个字符串是一种好习惯吗?还是有一个我不知道的问题?

ggo*_*zad 111

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

你是对的,虽然连接超过2个字符串+是一个O(n ^ 2)操作(与O(n)相比join),因此效率低下.然而,这与使用循环无关.甚至a + b + c + ...是O(n ^ 2),原因是每个连接产生一个新的字符串.

CPython2.4及更高版本试图减轻这种情况,但是join当连接超过2个字符串时仍然建议使用它.

  • @Mutant:`.join`采用迭代,所以`.join([a,b])`和`.join((a,b))`都是有效的. (5认同)

Mik*_*maa 47

Plus运算符是连接两个 Python字符串的完美解决方案.但是如果你继续添加两个以上的字符串(n> 25),你可能想要想别的东西.

''.join([a, b, c]) 技巧是性能优化.

  • 'n> 25`为+1.人类需要参考点才能从某个地方开始. (6认同)
  • 元组会更快 - 代码只是一个例子:)通常长多个字符串输入是动态的. (5认同)
  • 这里需要说的是:元组通常是SLOWER结构,特别是如果它正在增长.使用list,你可以使用list.extend(list_of_items)和list.append(item),它们在动态连接东西时要快得多. (5认同)
  • @martineau我认为他的意思是动态生成和`追加()字符串到列表. (4认同)
  • 一个元组不会比列表更好吗? (2认同)
  • 实际上取决于字符串的大小,而不是它们的数字.即使是3个字符串,如果字符串非常长,连接也会更好. (2认同)

Abh*_*jit 7

假设一个人永远不应该使用+进行字符串连接,而是总是使用'​​'.join可能是一个神话.确实,使用+创建不可变的字符串对象的不必要的临时副本,但另一个不引用的事实是,join在循环中调用通常会增加开销function call.让我们举个例子.

创建两个列表,一个来自链接的SO问题,另一个是更大的捏造

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

让我们创建两个函数,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)]
Run Code Online (Sandbox Code Playgroud)

让我们用第一个列表运行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
>>> 
Run Code Online (Sandbox Code Playgroud)

它们的运行时几乎相同.

让我们使用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}
Run Code Online (Sandbox Code Playgroud)

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

现在回到这个问题.在所有情况下,是否应该阻止使用+over join

我相信不,应该考虑事情

  1. 问题中字符串的长度
  2. 否连接操作.

在开发过程中,预先成熟的优化是邪恶的.

  • 当然,我们的想法是不在循环内部使用`join` - 而是循环将生成一个将被传递给join的序列. (7认同)

Izk*_*ata 7

与多人合作时,有时很难确切知道发生了什么.使用格式字符串而不是连接可以避免一个特定的烦恼发生在我们身上很多次:

比如说,一个函数需要一个参数,你写它期望得到一个字符串:

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

In [2]: foo('bang')
bar: bang
Run Code Online (Sandbox Code Playgroud)

因此,在整个代码中可能经常使用此函数.你的同事可能确切知道它的作用,但不一定完全在内部加速,并且可能不知道该函数需要一个字符串.所以他们最终可能会这样:

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

In [3]: foo(23)
bar: 23
Run Code Online (Sandbox Code Playgroud)

对于定义的所有类型的对象也是如此__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
Run Code Online (Sandbox Code Playgroud)

所以,是的:如果你可以使用格式字符串做到这一点,并好好利用一下Python有提供.

  • +1 对于有充分理由的反对意见。不过我仍然认为我更喜欢“+”。 (2认同)
  • 为什么不将 foo 方法定义为: print 'bar: ' + str(zeta) ? (2认同)
  • @EngineerWithJava54321 例如, `zeta = u"a\xac\u1234\u20ac\U00008000"` - 所以你必须使用 `print 'bar: ' + unicode(zeta)` 来确保它不会出错。`%s` 不用考虑就可以做到,而且要短得多 (2认同)

归档时间:

查看次数:

184508 次

最近记录:

8 年,1 月 前