Pythonの一般的なアンチパターンは、ループで+
を使用して文字列のシーケンスを連結することです。 Pythonインタープリターは反復ごとに新しい文字列オブジェクトを作成する必要があり、最終的に2次時間がかかるため、これは悪いことです。 (CPythonの最近のバージョンは明らかにこれを最適化できる場合もありますが、他の実装は最適化できないため、プログラマはこれに依存することをお勧めしません。)''.join
はこれを行う正しい方法です。
ただし、( スタックオーバーフローに関するここを含む )と言ったことを聞いたことがあります絶対に、文字列には+
連結しますが、代わりに常に''.join
またはフォーマット文字列を使用します。 2つの文字列を連結するだけの場合、これが当てはまる理由はわかりません。私の理解が正しければ、二次的な時間はかからないはずです。また、a + b
は''.join((a, b))
または'%s%s' % (a, b)
のいずれよりもクリーンで読みやすいと思います。
+
を使用して2つの文字列を連結することをお勧めしますか?または、私が知らない問題がありますか?
two文字列を+
で連結しても何も問題はありません。実際、''.join([a, b])
よりも読みやすいです。
ただし、3つ以上の文字列を+
と連結することはO(n ^ 2)操作であり(join
のO(n)と比較)、非効率的です。ただし、これはループの使用とは関係ありません。 a + b + c + ...
でさえO(n ^ 2)です。これは、各連結が新しい文字列を生成するためです。
CPython2.4以降はそれを緩和しようとしますが、3つ以上の文字列を連結するときにjoin
を使用することをお勧めします。
Plus演算子は、two Python文字列を連結するための完全に素晴らしいソリューションです。ただし、3つ以上の文字列(n> 25)を追加し続ける場合は、何か他のものを考えてみてください。
''.join([a, b, c])
トリックはパフォーマンスの最適化です。
文字列の連結に+を使用してはならないが、代わりに '' .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)]
UseJoin
およびUsePlus
の2つの関数を作成して、それぞれの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を使用すると、不要な関数呼び出しが発生し、オーバーヘッドが増加する可能性があるようです。
質問に戻りましょう。すべての場合において、join
よりも+
の使用をやめるべきですか?
いいえ、物事を考慮する必要があります
そして、開発の時期尚早な最適化のコース外は悪です。
複数の人と作業する場合、何が起こっているのかを正確に知ることが難しい場合があります。連結の代わりにフォーマット文字列を使用することで、私たちに何トンも発生する特定の煩わしさを回避できます。
たとえば、関数には引数が必要で、文字列を取得することを想定して記述します。
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
はい。フォーマット文字列do itを使用して、Pythonが提供するものを利用できる場合。
Pythonのドキュメントによると、str.join()を使用すると、Pythonのさまざまな実装でパフォーマンスの一貫性が得られます。 CPythonはs = s + tの2次動作を最適化しますが、他のPython実装はそうでない場合があります。
CPython実装の詳細:sとtが両方とも文字列の場合、CPythonなどの一部のPython実装は通常、インプレース最適化を実行できますs = s + tまたはs + = tの形式の割り当ての場合。該当する場合、この最適化により、2次実行時間が非常に少なくなります。この最適化は、バージョンと実装の両方に依存しています。パフォーマンスに敏感なコードの場合、バージョンおよび実装全体で一貫した線形連結パフォーマンスを保証するstr.join()メソッドを使用することをお勧めします。
Python docsのシーケンスタイプ (脚注[6]を参照)
私は簡単なテストを行いました:
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)時間を示しません。
したがって、少なくともパフォーマンスの観点からは、+
を使用するのが適切です。
''。join([a、b])は+よりも優れたソリューションです。
コードは、他のPython実装(PyPy、Jython、IronPython、Cython、Psycoなど)を不利にしない方法で記述する必要があるため
form a + = bまたはa = a + bはCPythonでも壊れやすく、実装にはまったく存在しません使用しないrefcounting(参照カウントは、オブジェクト、メモリブロック、ディスクスペース、その他のリソースなどのリソースへの参照、ポインター、またはハンドルの数を格納する手法です)
https://www.python.org/dev/peps/pep-0008/#programming-recommendations