次の方法以外に、Pythonで文字列を別の文字列に追加する効率的な方法が必要です。
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
使用できる組み込みの方法はありますか?
次の方法以外に、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;
}
経験的に検証するのは簡単です。
$ 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は短い文字列ではうまく機能するが、より大きな文字列ではうまく機能しないことをしています。
PyString_ConcatAndDel
関数を引用しましたが、のコメントが含まれてい_PyString_Resize
ます。また、コメントはBig-Oに関するあなたの主張を実際に確立するものではありません
"".join(str_a, str_b)
時期尚早に最適化しないでください。あなたは、文字列の連結による速度のボトルネックがあると信じる理由がない場合は、ちょうどにこだわる+
と+=
:
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)
それは読みやすく、より効率的です。
"<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())
大きな文字列を作成するために多くの追加操作を実行する必要がある場合は、StringIOまたはcStringIOを使用できます。インターフェースはファイルのようなものです。つまり、write
テキストを追加します。
2つの文字列を追加するだけの場合は、を使用します+
。
基本的に、違いはありません。唯一の一貫した傾向は、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秒
1.19 s
し992 ms
ています
__add__関数で文字列を追加する
str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)
出力
Hello World
str + str2
まだ短いです。
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
a.__add__(b)
は書くことと同じですa+b
。+
演算子を使用して文字列を連結すると、Pythonは__add__
左側の文字列のメソッドを呼び出し、右側の文字列をパラメーターとして渡します。
"foo" + "bar" + str(3)