2007-12-26
■[python]文字列の結合
文字列を結合するには + よりも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、Jython、IronPython、Pyrex、Psyco など)ごとの欠点を引き出さないように書くべきである。たとえば、CPython が a+=b や a=a+b などの文字列連結をインプレイス処理して、効率よく動作する実装に依存してはならない。これでは Jython での動作が遅くなってしまう。パフォーマンスに敏感な部分では、''.join() を使うべきである。こう書いておけば、様々な実装において、連結処理は線形時間で処理できる。
どうやらCPythonでは、join( )よりも+を使った方が速くなるように実装されているようです。ただし速いといっても僅かな差です。おそらく他の実装では、それ以上の差で+を使った文字連結は遅いのでしょう(本当はちゃんと調べるべきなんでしょうが、さすがにそこまでやるのは面倒です)。少なくとも、他人が使うようなプログラムでは、文字連結にはjoin( )を使えということでしょうか。
追記
まず、インプレイス処理されるのはa+=bの場合であって、a=a+bの場合ではないので、少なくとも1番目と2番目の計測結果とインプレイス処理は関係ないです。上記の記事で指摘されている通り、原因はタプル作成のオーバーヘッドです。そもそも、+での文字列結合が遅い原因は、結合のたびに新しい文字列が生成されることなのですから、当然タプル作成にかかる時間も考えるべきでした。id:morchinさん、ご指摘いただきありがとうございます。
- 3 http://search.yahoo.co.jp/search?p=rindex+python&x=0&y=0&fr=top_v2&tid=top_v2&ei=euc-jp&search.x=1
- 2 http://www.google.com/reader/view/
- 1 http://209.85.175.104/search?q=cache:NWJPIn2WnPMJ:d.hatena.ne.jp/yumimue/20071205/1196839438+python+list+extend&hl=ja&ct=clnk&cd=2&gl=jp&lr=lang_ja&client=firefox
- 1 http://d.hatena.ne.jp/johzan/
- 1 http://d.hatena.ne.jp/keyworddiary/python
- 1 http://d.hatena.ne.jp/shunsuk/20071225/1198590062
- 1 http://www.google.co.jp/search?hl=ja&q=python+辞書オブジェクト&btnG=検索&lr=
- 1 http://www.google.co.jp/search?q=python 例外 ファイル&lr=lang_ja&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official&client=firefox-a
- 1 http://www.google.com/search?q=python+辞書オブジェクト&sourceid=navclient-ff&ie=UTF-8&rlz=1B3GGGL_jaJP239JP239