web-dev-qa-db-ja.com

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

Pythonで文字列を別の文字列に追加する効率的な方法が欲しいのです。

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

使用するよい組み込み方法はありますか?

542
user469652

文字列への参照が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;
}

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

 xrange(10)のiに対する$ python -m timeit -s "s = ''" ":s + = 'a'" 
 1000000ループ、3/1.85 usec per loop [
 $ python -m timeit -s "s = ''" "xrange(100)のiの場合:s + = 'a'" 
 10000ループ、3/16.8 usec/loop 
 ____。] xrange(1000)のiの$ python -m timeit -s "s = ''" ":s + = 'a'" 
 10000ループ、最高3:158 usec /ループ
 。] $ python -m timeit -s "s = ''" "xrange(10000)のiの場合:s + = 'a'" 
 1000ループ、1ループあたり3:1.71ミリ秒が最適です
 ] $ python -m timeit -s "s = ''" "iのxrange(100000):s + = 'a'" 
 10ループ、1ループあたり3:14.6ミリ秒以下
 $ python -m timeit -s "s = ''" "xrange(1000000)のiの場合:s + = 'a'" 
 10ループ、1ループあたり3:173ミリ秒以下

ただし、この最適化はPythonの仕様の一部ではないことに注意することが重要です。私の知る限り、これはcPythonの実装の中だけです。例えば、pypyやjythonで同じ経験的テストを行ったところ、古いO(n ** 2)パフォーマンスが示される可能性があります。

 xrange(10)のiに対する$ pypy -m timeit -s "s = ''" ":s + = 'a'" 
ループあたり10000ループ、最高3:90.8 usec [ xrange(100)のiの$ pypy -m timeit -s "s = ''" ":s + = 'a'" 
 1000ループ、1ループあたり3:896 usec [。] ] $ pypy -m timeit -s xrange(1000)のiの場合:s + = 'a' "
 100ループ、1ループあたり3:9.03ミリ秒以下
 。] $ pypy -m timeit -s "s = ''" "xrange(10000)のiの場合:s + = 'a'" 
 10ループ、1ループあたり3:89.5ミリ秒以下。 ]

これまでのところ非常に良い

 xrange(100000)のiの$ pypy -m timeit -s "s = ''" ":s + = 'a'" 
 10ループ、1ループあたり3:12.8秒以下]

二次よりも痛いです。そのため、pypyは短い文字列ではうまくいくようなことをしていますが、大きな文字列ではうまく機能しません。

558
John La Rooy

時期尚早に最適化しないでください。文字列の連結が原因でスピードのボトルネックが発生すると信じる理由がない場合は、++=を使用してください。

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

とは言っても、JavaのStringBuilderのようなものを目指しているのなら、標準的なPythonの慣用句では、リストに項目を追加し、最後にstr.joinを使用してそれらをすべて連結します。

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

s = ''.join(l)
266
John Kugelman

しないでください。

つまり、ほとんどの場合、既存の文字列に追加するよりも、文字列全体を一度に生成する方が得策です。

たとえば、しないでください。obj1.name + ":" + str(obj1.count)

代わりに"%s:%d" % (obj1.name, obj1.count)を使用してください。

それは読みやすく、より効率的になります。

39
Winston Ewert
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

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

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

34
Rafe Kettler

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

2つの文字列を追加するだけの場合は、+を使用してください。

10

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

9
Ramy

Python 3.6では、 f文字列 が得られます。

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

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

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2
6
Trenton
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'
3

基本的には、違いはありません。唯一の一貫した傾向は、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.34s

Python 3.4

1ループ、3以下:ループあたり7.99s

Python 3.5

1ループ、3のうち最良:ループあたり8.48s

Python 3.6

1ループ、3以下:ループあたり9.93s


ひも

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

Python 2.7

1ループ、3つのうちの最も良い:ループごとに7.41 s

Python 3.4

1ループ、3以下:ループあたり9.08s

Python 3.5

1ループ、3以下:ループあたり8.82s

Python 3.6

1ループ、3以下:ループあたり9.24s

3
ostrokach

_ ADD _関数で文字列を追加する

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

出力

Hello World
3
saigopi