JavaのStringBuffer
のようなPythonに何かありますか?文字列はPythonでも不変なので、ループで編集するのは非効率的です。
Pythonでの効率的な文字列連結 はかなり古い記事であり、単純な連結は結合よりもはるかに遅いという主なステートメントはもはや有効ではありません。この部分はそれ以来CPythonで最適化されているためです:
CPython実装の詳細:sとtが両方とも文字列の場合、CPythonなどの一部のPython実装は通常、s = s + tまたはs + = tの形式の割り当てに対してインプレース最適化を実行できます該当する場合、この最適化は2次実行時間を大幅に削減しますこの最適化はバージョンと実装の両方に依存しますパフォーマンスに敏感なコードの場合、バージョン間で一貫した線形連結パフォーマンスを保証するstr.join()メソッドを使用することをお勧めします@ http://docs.python.org/2/library/stdtypes.html
私は彼らのコードを少し適合させ、私のマシンで次の結果を得ました:
from cStringIO import StringIO
from UserString import MutableString
from array import array
import sys, timeit
def method1():
out_str = ''
for num in xrange(loop_count):
out_str += `num`
return out_str
def method2():
out_str = MutableString()
for num in xrange(loop_count):
out_str += `num`
return out_str
def method3():
char_array = array('c')
for num in xrange(loop_count):
char_array.fromstring(`num`)
return char_array.tostring()
def method4():
str_list = []
for num in xrange(loop_count):
str_list.append(`num`)
out_str = ''.join(str_list)
return out_str
def method5():
file_str = StringIO()
for num in xrange(loop_count):
file_str.write(`num`)
out_str = file_str.getvalue()
return out_str
def method6():
out_str = ''.join([`num` for num in xrange(loop_count)])
return out_str
def method7():
out_str = ''.join(`num` for num in xrange(loop_count))
return out_str
loop_count = 80000
print sys.version
print 'method1=', timeit.timeit(method1, number=10)
print 'method2=', timeit.timeit(method2, number=10)
print 'method3=', timeit.timeit(method3, number=10)
print 'method4=', timeit.timeit(method4, number=10)
print 'method5=', timeit.timeit(method5, number=10)
print 'method6=', timeit.timeit(method6, number=10)
print 'method7=', timeit.timeit(method7, number=10)
結果:
2.7.1 (r271:86832, Jul 31 2011, 19:30:53)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)]
method1= 0.171155929565
method2= 16.7158739567
method3= 0.420584917068
method4= 0.231794118881
method5= 0.323612928391
method6= 0.120429992676
method7= 0.145267963409
結論:
join
は依然として連結に勝ちますが、わずかにおそらく bytearray を使用してください:
In [1]: s = bytearray('Hello World')
In [2]: s[:5] = 'Bye'
In [3]: s
Out[3]: bytearray(b'Bye World')
In [4]: str(s)
Out[4]: 'Bye World'
バイト配列を使用する魅力は、そのメモリ効率と便利な構文です。また、一時リストを使用するよりも高速です。
In [36]: %timeit s = list('Hello World'*1000); s[5500:6000] = 'Bye'; s = ''.join(s)
1000 loops, best of 3: 256 µs per loop
In [37]: %timeit s = bytearray('Hello World'*1000); s[5500:6000] = 'Bye'; str(s)
100000 loops, best of 3: 2.39 µs per loop
速度の違いの多くは、コンテナの作成に起因することに注意してください。
In [32]: %timeit s = list('Hello World'*1000)
10000 loops, best of 3: 115 µs per loop
In [33]: %timeit s = bytearray('Hello World'*1000)
1000000 loops, best of 3: 1.13 µs per loop
何をしたいかによって異なります。可変シーケンスが必要な場合、組み込みのlist
型はあなたの友人であり、strからlistに戻ったり戻ったりするのは簡単です:
mystring = "abcdef"
mylist = list(mystring)
mystring = "".join(mylist)
Forループを使用して大きな文字列を作成する場合、Pythonの方法は通常、文字列のリストを作成し、適切な区切り記号(改行またはその他)で結合します。
それ以外の場合は、テキストテンプレートシステム、パーサー、またはジョブに最適な専用ツールを使用することもできます。
以前に提供された回答は、ほとんど常に最高です。ただし、多くのメソッド呼び出しやループで文字列が作成される場合があるため、行のリストを作成してからそれらを結合することは必ずしも自然ではありません。また、CPythonを使用していることやCPythonの最適化が適用されることを保証するものではないので、別のアプローチはprintを使用することです!
ヘルパークラスの例は次のとおりです。ヘルパークラスは簡単でおそらくおそらく不要ですが、アプローチを説明するのに役立ちます(Python 3)。
import io
class StringBuilder(object):
def __init__(self):
self._stringio = io.StringIO()
def __str__(self):
return self._stringio.getvalue()
def append(self, *objects, sep=' ', end=''):
print(*objects, sep=sep, end=end, file=self._stringio)
sb = StringBuilder()
sb.append('a')
sb.append('b', end='\n')
sb.append('c', 'd', sep=',', end='\n')
print(sb) # 'ab\nc,d\n'
python 3.6.2で実行したテストで、「結合」がまだ大勝していることを示しています。
from time import time
def _with_format(i):
_st = ''
for i in range(0, i):
_st = "{}{}".format(_st, "0")
return _st
def _with_s(i):
_st = ''
for i in range(0, i):
_st = "%s%s" % (_st, "0")
return _st
def _with_list(i):
l = []
for i in range(0, i):
l.append("0")
return "".join(l)
def _count_time(name, i, func):
start = time()
r = func(i)
total = time() - start
print("%s done in %ss" % (name, total))
return r
iterationCount = 1000000
r1 = _count_time("with format", iterationCount, _with_format)
r2 = _count_time("with s", iterationCount, _with_s)
r3 = _count_time("with list and join", iterationCount, _with_list)
if r1 != r2 or r2 != r3:
print("Not all results are the same!")
出力は次のとおりです。
with format done in 17.991968870162964s
with s done in 18.36879801750183s
with list and join done in 0.12142801284790039s
このリンクは、Pythonでの連結に役立つ場合があります
http://pythonadventures.wordpress.com/2010/09/27/stringbuilder/
上記のリンクの例:
def g():
sb = []
for i in range(30):
sb.append("abcdefg"[i%7])
return ''.join(sb)
print g()
# abcdefgabcdefgabcdefgabcdefgab
Roee Gavirelのコード2に、リストを文字列に結合することがs + = "something"より速くないことを最終的に示す追加のテストを追加しました。
結果:
Python 2.7.15rc1
Iterations: 100000
format done in 0.317540168762s
%s done in 0.151262044907s
list+join done in 0.0055148601532s
str cat done in 0.00391721725464s
Python 3.6.7
Iterations: 100000
format done in 0.35594654083251953s
%s done in 0.2868080139160156s
list+join done in 0.005924701690673828s
str cat done in 0.0054128170013427734s
f str done in 0.12870001792907715s
コード:
from time import time
def _with_cat(i):
_st = ''
for i in range(0, i):
_st += "0"
return _st
def _with_f_str(i):
_st = ''
for i in range(0, i):
_st = f"{_st}0"
return _st
def _with_format(i):
_st = ''
for i in range(0, i):
_st = "{}{}".format(_st, "0")
return _st
def _with_s(i):
_st = ''
for i in range(0, i):
_st = "%s%s" % (_st, "0")
return _st
def _with_list(i):
l = []
for i in range(0, i):
l.append("0")
return "".join(l)
def _count_time(name, i, func):
start = time()
r = func(i)
total = time() - start
print("%s done in %ss" % (name, total))
return r
iteration_count = 100000
print('Iterations: {}'.format(iteration_count))
r1 = _count_time("format ", iteration_count, _with_format)
r2 = _count_time("%s ", iteration_count, _with_s)
r3 = _count_time("list+join", iteration_count, _with_list)
r4 = _count_time("str cat ", iteration_count, _with_cat)
r5 = _count_time("f str ", iteration_count, _with_f_str)
if len(set([r1, r2, r3, r4, r5])) != 1:
print("Not all results are the same!")