Pythonで文字列を別の文字列に追加する効率的な方法が欲しいのです。
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
使用するよい組み込み方法はありますか?
文字列への参照が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は短い文字列ではうまくいくようなことをしていますが、大きな文字列ではうまく機能しません。
時期尚早に最適化しないでください。文字列の連結が原因でスピードのボトルネックが発生すると信じる理由がない場合は、+
と+=
を使用してください。
s = 'foo'
s += 'bar'
s += 'baz'
とは言っても、JavaのStringBuilderのようなものを目指しているのなら、標準的なPythonの慣用句では、リストに項目を追加し、最後にstr.join
を使用してそれらをすべて連結します。
l = []
l.append('foo')
l.append('bar')
l.append('baz')
s = ''.join(l)
しないでください。
つまり、ほとんどの場合、既存の文字列に追加するよりも、文字列全体を一度に生成する方が得策です。
たとえば、しないでください。obj1.name + ":" + str(obj1.count)
代わりに"%s:%d" % (obj1.name, obj1.count)
を使用してください。
それは読みやすく、より効率的になります。
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
これはstr1とstr2を区切り文字としてスペースで結合します。 "".join(str1, str2, ...)
もできます。 str.join()
はイテラブルを取るので、文字列をリストかTupleに入れる必要があります。
それは組み込みメソッドの場合と同じくらい効率的です。
大きな文字列を作成するために多くの追加操作を行う必要がある場合は、 StringIO またはcStringIOを使用できます。インターフェースはファイルのようなものです。すなわち:あなたはそれにテキストを追加するためにwrite
を使います。
2つの文字列を追加するだけの場合は、+
を使用してください。
それは本当にあなたのアプリケーションに依存します。何百もの単語をループしていて、それらすべてをリストに追加したい場合は、.join()
が優れています。しかし、長い文章をまとめる場合は、+=
を使用したほうがよいでしょう。
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
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
基本的には、違いはありません。唯一の一貫した傾向は、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
_ ADD _関数で文字列を追加する
str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)
出力
Hello World