414

Pythonstringは変更できないので、文字列をより効率的に連結する方法を考えていました。

私はそれのように書くことができます:

s += stringfromelsewhere

またはこのように:

s = []

s.append(somestring)
    
# later
    
s = ''.join(s)

この質問を書いている間、私はそのトピックについて話している良い記事を見つけました。

http://www.skymind.com/~ocrow/python_string/

しかし、それはPython 2.xにあるので、問題はPython 3で何か変更があったのでしょうか?

4

12 に答える 12

480

文字列変数に文字列を追加する最良の方法は、 +orを使用すること+=です。これは、読みやすく高速であるためです。どちらを選択するかは好みの問題ですが、後者が最も一般的です。timeitモジュールのタイミングは次のとおりです。

a = a + b:
0.11338996887207031
a += b:
0.11040496826171875

ただし、リストを作成してそれらに追加し、それらのリストに結合することを推奨する人は、リストに文字列を追加する方が文字列を拡張するよりもおそらく非常に高速であるため、そうします。そして、これは場合によっては真実です。たとえば、1 文字の文字列を最初に文字列に追加し、次にリストに追加する 100 万回を次に示します。

a += b:
0.10780501365661621
a.append(b):
0.1123361587524414

OK、結果の文字列が 100 万文字の長さであっても、追加の方が高速であることがわかりました。

それでは、1,000 文字の長い文字列を 10 万回追加してみましょう。

a += b:
0.41823482513427734
a.append(b):
0.010656118392944336

したがって、最後の文字列は約 100MB の長さになります。それはかなり遅かったのですが、リストへの追加ははるかに高速でした。そのタイミングには最終的なa.join(). それで、それにはどれくらい時間がかかりますか?

a.join(a):
0.43739795684814453

おっと。この場合でも、追加/結合は遅くなります。

では、この推奨事項はどこから来たのでしょうか。パイソン2?

a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474

非常に長い文字列を使用している場合、追加/結合はわずかに高速です (通常はそうではありません。メモリに 100MB の文字列があるとしたらどうでしょうか?)

しかし、本当の決め手は Python 2.3 です。まだ終わっていないほど遅いので、タイミングを示すことさえしません。これらのテストには突然数分かかります。追加/結合を除いて、これは後のPythonと同じくらい高速です。

うん。石器時代の Python では、文字列の連結は非常に遅かったです。しかし、2.4 ではもう (または少なくとも Python 2.4.7 では) そうではありません。そのため、Python 2.3 の更新が停止された 2008 年に追加/結合を使用するという推奨事項は時代遅れになり、使用を停止する必要がありました。:-)

(更新: Python 2.3 で 2 つの文字列を使用するよりも慎重にテストを行ったところ、より高速+であることがわかりました。使用することをお勧めするのは誤解である必要があります)+=''.join()

ただし、これは CPython です。他の実装には他の問題があるかもしれません。これが、時期尚早の最適化が諸悪の根源であるもう 1 つの理由です。最初に測定しない限り、「より速い」と思われる手法を使用しないでください。

したがって、文字列連結を行うための「最良の」バージョンは、 + または += を使用することです。そして、それが遅いことが判明した場合は (ほとんどありそうにありません)、別のことをしてください。

では、なぜコードで多くの追加/結合を使用するのでしょうか? 時々、実際にはより明確になるからです。特に、連結する必要があるものをスペース、コンマ、または改行で区切る必要がある場合。

于 2012-08-29T05:24:50.297 に答える
56

多くの値を連結している場合は、どちらでもありません。リストの追加には費用がかかります。そのためにStringIOを使用できます。特に、多くの操作で構築している場合はなおさらです。

from cStringIO import StringIO
# python3:  from io import StringIO

buf = StringIO()

buf.write('foo')
buf.write('foo')
buf.write('foo')

buf.getvalue()
# 'foofoofoo'

他の操作から完全なリストが既に返されている場合は、''.join(aList)

Python FAQから:多くの文字列を連結する最も効率的な方法は何ですか?

strオブジェクトとbytesオブジェクトは不変であるため、多くの文字列を連結すると、各連結によって新しいオブジェクトが作成されるため、非効率的です。一般的なケースでは、合計ランタイムコストは文字列の合計長の2次式です。

多くのstrオブジェクトを蓄積するには、それらをリストに配置し、最後にstr.join()を呼び出すことをお勧めします。

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)

(もう1つの合理的に効率的なイディオムは、io.StringIOを使用することです)

多くのバイトオブジェクトを蓄積するために推奨されるイディオムは、インプレース連結(+ =演算子)を使用してbytearrayオブジェクトを拡張することです。

result = bytearray()
for b in my_bytes_objects:
    result += b

編集:私はばかげていて、結果を逆方向に貼り付けて、リストへの追加がcStringIOよりも高速であるように見せました。また、bytearray / str concatのテストと、より大きな文字列を含むより大きなリストを使用した2回目のテストも追加しました。(python 2.7.3)

文字列の大きなリストのipythonテスト例

try:
    from cStringIO import StringIO
except:
    from io import StringIO

source = ['foo']*1000

%%timeit buf = StringIO()
for i in source:
    buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop

%%timeit out = []
for i in source:
    out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop

%%timeit out = bytearray()
for i in source:
    out += i
# 10000 loops, best of 3: 98.5 µs per loop

%%timeit out = ""
for i in source:
    out += i
# 10000 loops, best of 3: 161 µs per loop

## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching 
## done by the Python
source = ['foo']*1000

# cStringIO
# 10 loops, best of 3: 19.2 ms per loop

# list append and join
# 100 loops, best of 3: 144 ms per loop

# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop

# str() +=
# 100 loops, best of 3: 5.11 ms per loop
于 2012-08-29T01:48:41.507 に答える
18

「+」による文字列連結をその場で使用することは、すべての値をサポートしていないため、安定性とクロス実装の点で最悪の連結方法です。PEP8 標準はこれを推奨せず、長期間使用するために format()、join()、および append() の使用を推奨しています。

リンクされた「プログラミングの推奨事項」セクションから引用したとおり:

たとえば、a += b または a = a + b の形式のステートメントのインプレース文字列連結の CPython の効率的な実装に依存しないでください。この最適化は CPython でも壊れやすく (一部の型でしか機能しません)、refcounting を使用しない実装にはまったく存在しません。ライブラリのパフォーマンスが重要な部分では、代わりに ''.join() フォームを使用する必要があります。これにより、さまざまな実装間で連結が線形時間で行われることが保証されます。

于 2014-07-15T13:40:38.347 に答える
8

推奨される方法は、引き続き追加と結合を使用することです。

于 2012-08-29T01:48:02.503 に答える
8

連結する文字列がリテラルの場合は、文字列リテラル連結を使用します

re.compile(
        "[A-Za-z_]"       # letter or underscore
        "[A-Za-z0-9_]*"   # letter, digit or underscore
    )

これは、(上記のように) 文字列の一部にコメントを付けたい場合や、すべてではなくリテラルの一部に生の文字列または三重引用符を使用したい場合に便利です。

これは構文レイヤーで発生するため、ゼロ連結演算子を使用します。

于 2016-08-06T17:21:02.110 に答える
7

さまざまな方法で行うことができます。

str1 = "Hello"
str2 = "World"
str_list = ['Hello', 'World']
str_dict = {'str1': 'Hello', 'str2': 'World'}

# Concatenating With the + Operator
print(str1 + ' ' + str2)  # Hello World

# String Formatting with the % Operator
print("%s %s" % (str1, str2))  # Hello World

# String Formatting with the { } Operators with str.format()
print("{}{}".format(str1, str2))  # Hello World
print("{0}{1}".format(str1, str2))  # Hello World
print("{str1} {str2}".format(str1=str_dict['str1'], str2=str_dict['str2']))  # Hello World
print("{str1} {str2}".format(**str_dict))  # Hello World

# Going From a List to a String in Python With .join()
print(' '.join(str_list))  # Hello World

# Python f'strings --> 3.6 onwards
print(f"{str1} {str2}")  # Hello World

私は次の記事を通してこの小さな要約を作成しました。

于 2019-06-24T11:07:50.140 に答える
6

やや時代遅れですが、Pythonista のようなコード: Idiomatic Pythonでは、このセクションを推奨join()しています。文字列連結に関するセクションのPythonSpeedPerformanceTipsと同様に、次の免責事項があります。+

このセクションの正確性は、Python の新しいバージョンに関して議論されています。CPython 2.5 では、文字列連結はかなり高速ですが、これは他の Python 実装には当てはまらない場合があります。詳細については、ConcatenationTestCode を参照してください。

于 2012-08-29T01:57:35.450 に答える