135

Pythonの一般的なアンチパターンは+、ループで使用して文字列のシーケンスを連結することです。Pythonインタープリターは反復ごとに新しい文字列オブジェクトを作成する必要があり、2次の時間がかかるため、これは悪いことです。(最近のバージョンのCPythonは、場合によってはこれを最適化できるようですが、他の実装では最適化できないため、プログラマーはこれに依存することをお勧めしません。)''.joinこれを行う正しい方法です。

ただし、文字列の連結には絶対に使用せず、代わりに常にまたはフォーマット文字列を使用する必要があるとのことです(ここではStack Overflowを含む) 。2つの文字列のみを連結している場合、なぜこれが当てはまるのかわかりません。私の理解が正しければ、2次の時間はかからないはずです。また、またはのどちらよりもクリーンで読みやすいと思います。+''.joina + b''.join((a, b))'%s%s' % (a, b)

+2つの文字列を連結するために使用するのは良い習慣ですか?それとも私が気付いていない問題がありますか?

4

8 に答える 8

130

2つの文字列を。で連結しても問題はありません+。確かに、より読みやすいです''.join([a, b])

2つ以上の文字列を連結することは+O(n ^ 2)操作(のO(n)と比較してjoin)であり、したがって非効率的になりますが、あなたは正しいです。ただし、これはループの使用とは関係ありません。a + b + c + ...O(n ^ 2)でも、各連結によって新しい文字列が生成されるためです。

CPython2.4以降はそれを軽減しようとしますがjoin、2つ以上の文字列を連結する場合は使用することをお勧めします。

于 2012-04-06T13:11:17.533 に答える
51

Plus演算子は、2つのPython文字列を連結するための完全に優れたソリューションです。ただし、3つ以上の文字列(n> 25)を追加し続ける場合は、別のことを考えたいと思うかもしれません。

''.join([a, b, c])トリックはパフォーマンスの最適化です。

于 2012-04-06T12:46:04.690 に答える
8

文字列の連結に+を使用してはならず、代わりに常に''.joinを使用するという仮定は神話かもしれません。を使用+すると、不変の文字列オブジェクトの不要な一時コピーが作成されることは事実ですが、他のよく引用されない事実はjoin、ループで呼び出すと、通常、のオーバーヘッドが追加されることですfunction call。あなたの例を見てみましょう。

2つのリストを作成します。1つはリンクされたSOの質問からのもので、もう1つはより大きな作成されたものです。

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

2つの関数を作成し、それぞれと機能を使用してみUseJoinましょう。UsePlusjoin+

>>> 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. 連結操作はありません。

そして、開発の時期尚早な最適化のコース外は悪です。

于 2012-04-06T13:24:42.640 に答える
8

複数の人と作業する場合、何が起こっているのかを正確に知ることが難しい場合があります。連結の代わりにフォーマット文字列を使用すると、私たちに何度も発生する特定の煩わしさを回避できます。

たとえば、関数には引数が必要であり、文字列を取得することを期待して関数を記述します。

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が提供するものを利用してください。

于 2012-04-06T14:28:57.400 に答える
4

Pythonのドキュメントによると、str.join()を使用すると、Pythonのさまざまな実装間でパフォーマンスの一貫性が得られます。CPythonはs=s + tの二次動作を最適化しますが、他のPython実装はそうではない場合があります。

CPython実装の詳細:sとtが両方とも文字列である場合、CPythonなどの一部のPython実装は通常、s = s+tまたはs+=tの形式の割り当てに対してインプレース最適化を実行できます。該当する場合、この最適化により、2次実行時間が大幅に短縮されます。この最適化は、バージョンと実装の両方に依存します。パフォーマンスに敏感なコードの場合、バージョンと実装間で一貫した線形連結パフォーマンスを保証するstr.join()メソッドを使用することをお勧めします。

Pythonドキュメントのシーケンスタイプ(脚注[6]を参照)

于 2017-01-04T12:49:41.843 に答える
3

私は簡単なテストを行いました:

import sys

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

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)時間を示しません。

したがって、少なくともパフォーマンスの観点からは、使用+は問題ありません。

于 2012-04-06T13:28:06.730 に答える
3

私はPython3.8で以下を使用します

string4 = f'{string1}{string2}{string3}'
于 2020-04-18T15:04:38.663 に答える
1

'' .join([a、b])は+よりも優れたソリューションです。

コードは、Pythonの他の実装(PyPy、Jython、IronPython、Cython、Psycoなど)に不利にならないように作成する必要があるためです。

form a +=bまたはa=a + bは、CPythonでも壊れやすく、refcountingを使用しない実装にはまったく存在しません (参照カウントは、aへの参照、ポインター、またはハンドルの数を格納する手法です。オブジェクト、メモリブロック、ディスクスペース、その他のリソースなどのリソース

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

于 2016-04-29T12:34:28.020 に答える