文字列の結合

文字列を結合するには + よりもjoin( )を使った方が速いというので調べてみました。

from timeit import Timer

setup = 's = "xxxxxxxxxx"'
add_stmt = "s + s + s + s + s"
join_stmt = '"".join((s, s, s, s, s))'

print Timer(add_stmt, setup).repeat(3, 100000)
print Timer(join_stmt, setup).repeat(3, 100000)
[0.54150170133592579, 0.56340116327796308, 0.53983221307765805]
[0.75089341088519768, 0.75319901439849835, 0.76125898858512553]

join( )の方が遅いです。


次はもっと大きな文字列を結合してみます。

from timeit import Timer

setup = 's = "xxxxxxxxxx" * 100'
add_stmt = "s + s + s + s + s"
join_stmt = '"".join((s, s, s, s, s, s))'

print Timer(add_stmt, setup).repeat(3, 100000)
print Timer(join_stmt, setup).repeat(3, 100000)
[17.057702106974638, 17.127017717360332, 17.089626041334924]
[20.322900149181187, 20.214780670141977, 20.602317336864516]

こちらもjoin( )の方が遅いです。


次は結合回数を増やしてみます。

from timeit import Timer

add_stmt = """
x = ''
for i in xrange(100):
    x += str(i)
"""
join_stmt = """
x = []
for i in xrange(100):
    x.append(str(i))
"".join(x)
"""

print Timer(add_stmt).repeat(3, 10000)
print Timer(join_stmt).repeat(3, 10000)
[12.010844130810103, 11.956025075847736, 11.927375584572321]
[15.954444425820078, 16.054682445230391, 15.967345245478469]

こちらもjoin( )の方が遅いです。


リスト内包表記を使ってみます。

from timeit import Timer

add_stmt = """
x = ''
for i in xrange(100):
    x += str(i)
"""
join_stmt = "''.join([str(i) for i in xrange(100)])"

print Timer(add_stmt).repeat(3, 10000)
print Timer(join_stmt).repeat(3, 10000)
[11.105604351397107, 11.103802443889437, 11.08747213329087]
[11.204432692468863, 11.100584991367604, 11.122759348966625]

こちらはほぼ同じです。


計測の仕方が間違っているのでしょうか。どう見ても、+での文字連結の方が速いです。少し調べてみたところ、PEP 8に以下のような記述がありました。

ソースコードPython の実装(PyPy、JythonIronPython、Pyrex、Psyco など)ごとの欠点を引き出さないように書くべきである。たとえば、CPython が a+=b や a=a+b などの文字列連結をインプレイス処理して、効率よく動作する実装に依存してはならない。これでは Jython での動作が遅くなってしまう。パフォーマンスに敏感な部分では、''.join() を使うべきである。こう書いておけば、様々な実装において、連結処理は線形時間で処理できる。

どうやらCPythonでは、join( )よりも+を使った方が速くなるように実装されているようです。ただし速いといっても僅かな差です。おそらく他の実装では、それ以上の差で+を使った文字連結は遅いのでしょう(本当はちゃんと調べるべきなんでしょうが、さすがにそこまでやるのは面倒です)。少なくとも、他人が使うようなプログラムでは、文字連結にはjoin( )を使えということでしょうか。

追記

文字列結合 (2) - プログラミング日記
まず、インプレイス処理されるのはa+=bの場合であって、a=a+bの場合ではないので、少なくとも1番目と2番目の計測結果とインプレイス処理は関係ないです。上記の記事で指摘されている通り、原因はタプル作成のオーバーヘッドです。そもそも、+での文字列結合が遅い原因は、結合のたびに新しい文字列が生成されることなのですから、当然タプル作成にかかる時間も考えるべきでした。id:morchinさん、ご指摘いただきありがとうございます。