web-dev-qa-db-ja.com

Pythonと同等Java StringBuffer?

JavaのStringBufferのようなPythonに何かありますか?文字列はPythonでも不変なので、ループで編集するのは非効率的です。

63
user2902773

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は依然として連結に勝ちますが、わずかに
  • リストの内包表記はループよりも高速です
  • ジェネレータの結合はリストの結合よりも遅い
  • 他の方法は役に立ちません(何か特別なことをしているのでない限り)
67
georg

おそらく 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
11
unutbu

何をしたいかによって異なります。可変シーケンスが必要な場合、組み込みのlist型はあなたの友人であり、strからlistに戻ったり戻ったりするのは簡単です:

 mystring = "abcdef"
 mylist = list(mystring)
 mystring = "".join(mylist)

Forループを使用して大きな文字列を作成する場合、Pythonの方法は通常、文字列のリストを作成し、適切な区切り記号(改行またはその他)で結合します。

それ以外の場合は、テキストテンプレートシステム、パーサー、またはジョブに最適な専用ツールを使用することもできます。

11

以前に提供された回答は、ほとんど常に最高です。ただし、多くのメソッド呼び出しやループで文字列が作成される場合があるため、行のリストを作成してからそれらを結合することは必ずしも自然ではありません。また、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'
5
rhaertel80

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
2
Roee Gavirel

このリンクは、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
2
Kamlesh Arya

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!")
1
Martlark