Pythonで文字列を別の文字列に追加するにはどうすればよいですか?


594

次の方法以外に、Pythonで文字列を別の文字列に追加する効率的な方法が必要です。

var1 = "foo"
var2 = "bar"
var3 = var1 + var2

使用できる組み込みの方法はありますか?


8
TL; DR:文字列を追加する簡単な方法を探しているだけで、効率を気にしていない場合:"foo" + "bar" + str(3)
Andrew

回答:


609

文字列への参照が1つしかなく、別の文字列を最後に連結する場合、CPythonはこれを特殊なケースにして、文字列を適切な場所に拡張しようとします。

最終結果は、操作が償却O(n)になることです。

例えば

s = ""
for i in range(n):
    s+=str(i)

以前はO(n ^ 2)でしたが、現在はO(n)です。

ソースから(bytesobject.c):

void
PyBytes_ConcatAndDel(register PyObject **pv, register PyObject *w)
{
    PyBytes_Concat(pv, w);
    Py_XDECREF(w);
}


/* The following function breaks the notion that strings are immutable:
   it changes the size of a string.  We get away with this only if there
   is only one module referencing the object.  You can also think of it
   as creating a new string object and destroying the old one, only
   more efficiently.  In any case, don't use this if the string may
   already be known to some other part of the code...
   Note that if there's not enough memory to resize the string, the original
   string object at *pv is deallocated, *pv is set to NULL, an "out of
   memory" exception is set, and -1 is returned.  Else (on success) 0 is
   returned, and the value in *pv may or may not be the same as on input.
   As always, an extra byte is allocated for a trailing \0 byte (newsize
   does *not* include that), and a trailing \0 byte is stored.
*/

int
_PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
{
    register PyObject *v;
    register PyBytesObject *sv;
    v = *pv;
    if (!PyBytes_Check(v) || Py_REFCNT(v) != 1 || newsize < 0) {
        *pv = 0;
        Py_DECREF(v);
        PyErr_BadInternalCall();
        return -1;
    }
    /* XXX UNREF/NEWREF interface should be more symmetrical */
    _Py_DEC_REFTOTAL;
    _Py_ForgetReference(v);
    *pv = (PyObject *)
        PyObject_REALLOC((char *)v, PyBytesObject_SIZE + newsize);
    if (*pv == NULL) {
        PyObject_Del(v);
        PyErr_NoMemory();
        return -1;
    }
    _Py_NewReference(*pv);
    sv = (PyBytesObject *) *pv;
    Py_SIZE(sv) = newsize;
    sv->ob_sval[newsize] = '\0';
    sv->ob_shash = -1;          /* invalidate cached hash value */
    return 0;
}

経験的に検証するのは簡単です。

$ python -m timeit -s "s = ''" "for i in xrange(10):s + = 'a'"
1000000ループ、ベスト3:ループあたり1.85 usec
$ python -m timeit -s "s = ''" "for i in xrange(100):s + = 'a'"
10000ループ、ベスト3:ループあたり16.8 usec
$ python -m timeit -s "s = ''" "for i in xrange(1000):s + = 'a'"
10000ループ、ベスト3:ループあたり158 usec
$ python -m timeit -s "s = ''" "for i in xrange(10000):s + = 'a'"
1000ループ、最高3:ループあたり1.71ミリ秒
$ python -m timeit -s "s = ''" "for i in xrange(100000):s + = 'a'"
10ループ、最高3:ループあたり14.6ミリ秒
$ python -m timeit -s "s = ''" "for i in xrange(1000000):s + = 'a'"
10ループ、最高3:ループあたり173ミリ秒

それは重要です、この最適化は、Pythonの仕様の一部ではないことに注意することが。私の知る限り、これはcPython実装にのみ存在します。たとえば、pypyまたはjythonでの同じ経験的テストでは、古いO(n ** 2)パフォーマンスが示される場合があります。

$ pypy -m timeit -s "s = ''" "for i in xrange(10):s + = 'a'"
10000ループ、ベスト3:ループあたり90.8 usec
$ pypy -m timeit -s "s = ''" "for i in xrange(100):s + = 'a'"
1000ループ、最高3:ループあたり896 usec
$ pypy -m timeit -s "s = ''" "for i in xrange(1000):s + = 'a'"
100ループ、最高3:ループあたり9.03ミリ秒
$ pypy -m timeit -s "s = ''" "for i in xrange(10000):s + = 'a'"
10ループ、最高3:ループあたり89.5ミリ秒

これまでのところ、良いですが、

$ pypy -m timeit -s "s = ''" "for i in xrange(100000):s + = 'a'"
10ループ、最高3:ループあたり12.8秒

二次よりもさらに悪い。したがって、pypyは短い文字列ではうまく機能するが、より大きな文字列ではうまく機能しないことをしています。


14
面白い。「今」とは、Python 3.xを意味しますか?
スティーブTjoa

10
@スティーブ、いいえ。それは少なくとも2.6でおそらく2.5でもあります
John La Rooy

8
PyString_ConcatAndDel関数を引用しましたが、のコメントが含まれてい_PyString_Resizeます。また、コメントはBig-Oに関するあなたの主張を実際に確立するものではありません
Winston Ewert 2012年

3
コードが他の実装でクロールされるようにするCPython機能の活用についておめでとうございます。悪いアドバイス。
ジャン=フランソワ・ファーブル

4
これは使用しないでください。Pep8は明示的に述べています。コードは、Pythonの他の実装(PyPy、Jython、IronPython、Cython、Psycoなど)を損なうことのない方法で記述する必要があります。この場合、この特定の例は、非常に壊れやすいため、避けるべきものとして示しています。"".join(str_a, str_b)
1919年

287

時期尚早に最適化しないでください。あなたは、文字列の連結による速度のボトルネックがあると信じる理由がない場合は、ちょうどにこだわる++=

s  = 'foo'
s += 'bar'
s += 'baz'

つまり、JavaのStringBuilderのようなものを目指している場合、標準的なPythonのイディオムは、リストに項目を追加str.joinし、最後にそれらをすべて連結するために使用することです。

l = []
l.append('foo')
l.append('bar')
l.append('baz')

s = ''.join(l)

文字列をリストとして作成し、それらを.join()することによる速度への影響が何であるかはわかりませんが、一般的には最もクリーンな方法だと思います。また、私が作成したSQLテンプレートエンジンの文字列内で%s表記を使用することで、大きな成功を収めました。
richo

25
@Richo .joinの使用はより効率的です。その理由は、Pythonの文字列は不変であるため、s + = moreを繰り返し使用すると、連続してより大きな文字列が多数割り当てられます。.joinは、構成要素から1回で最終的な文字列を生成します。
Ben

5
@ベン、この分野で大幅な改善がありました-私の回答をご覧ください
John La Rooy

41
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

これは、区切り文字としてスペースを使用してstr1とstr2を結合します。あなたも行うことができます"".join(str1, str2, ...)str.join()イテラブルを取るので、文字列をリストまたはタプルに入れる必要があります。

これは、組み込みメソッドの場合と同じくらい効率的です。


str1が空の場合はどうなりますか?空白が設定されますか?
ユルゲン・K.

38

しないでください。

つまり、ほとんどの場合、既存の文字列に追加するのではなく、文字列全体を一度に生成する方が適切です。

たとえば、次のことは行わないでください。 obj1.name + ":" + str(obj1.count)

代わりに、使用 "%s:%d" % (obj1.name, obj1.count)

それは読みやすく、より効率的です。


54
最初の例のように(string + string)よりも読みやすいものはありません。2番目の例の方が効率的ですが、読み
にくい

23
@ ExceptionSlayer、string + stringは非常に簡単に追跡できます。しかし"<div class='" + className + "' id='" + generateUniqueId() + "'>" + message_text + "</div>"、私はそれより読みにくく、エラーが発生しやすいと感じています"<div class='{classname}' id='{id}'>{message_text}</div>".format(classname=class_name, message_text=message_text, id=generateUniqueId())
Winston Ewert

これは、PHP / perlの「string。= verifydata()」などとほぼ同じことをしようとしている場合にはまったく役に立ちません。
Shadur 2016

@Shadur、私のポイントは、あなたはもう一度考える必要があるということです、あなたは本当に同等の何かをしたいのですか、それとも完全に異なるアプローチの方が良いですか?
Winston Ewert 2016

1
そしてこの場合、その質問への答えは「いいえ、そのアプローチは私のユースケースをカバーしていないためです」
Shadur

11

Python 3.6は、f-stringsを提供してくれます

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar

あなたは中括弧の中でほとんど何でもすることができます

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2

10

大きな文字列を作成するために多くの追加操作を実行する必要がある場合は、StringIOまたはcStringIOを使用できます。インターフェースはファイルのようなものです。つまり、writeテキストを追加します。

2つの文字列を追加するだけの場合は、を使用します+


9

それは本当にあなたのアプリケーションに依存します。何百もの単語をループしていて、それらをすべてリストに追加したい場合.join()は、より良い方法です。ただし、長い文章をまとめる場合は、を使用し+=た方がよいでしょう。


5

基本的に、違いはありません。唯一の一貫した傾向は、Pythonがすべてのバージョンで遅くなっているように見えることです... :(


リスト

%%timeit
x = []
for i in range(100000000):  # xrange on Python 2.7
    x.append('a')
x = ''.join(x)

Python 2.7

1ループ、最高3:ループあたり7.34

Python 3.4

1ループ、最高3:ループあたり7.99

Python 3.5

1ループ、最高3:ループあたり8.48

Python 3.6

1ループ、最高3:ループあたり9.93


ストリング

%%timeit
x = ''
for i in range(100000000):  # xrange on Python 2.7
    x += 'a'

Python 2.7

1ループ、最高3:ループあたり7.41秒

Python 3.4

1ループ、最高3:ループあたり9.08

Python 3.5

1ループ、最高3:ループあたり8.82

Python 3.6

1ループ、最高3:ループあたり9.24


2
場合によります。私はそれぞれPython2.7 を取得1.19 s992 msています
John La Rooy

5

__add__関数で文字列を追加する

str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)

出力

Hello World

4
str + str2まだ短いです。
Nik O'Lai 2018年

2
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'

1
コードはいいですが、一緒に説明すると役立ちます。このページの他の回答ではなく、なぜこの方法を使用するのですか?
cgmb 2015年

11
使用することa.__add__(b)は書くことと同じですa+b+演算子を使用して文字列を連結すると、Pythonは__add__左側の文字列のメソッドを呼び出し、右側の文字列をパラメーターとして渡します。
Addie
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.