Я хочу ефективний спосіб додавання однієї рядка до іншої в Python, крім наступного.
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Чи є якийсь хороший вбудований метод для використання?
Я хочу ефективний спосіб додавання однієї рядка до іншої в Python, крім наступного.
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Чи є якийсь хороший вбудований метод для використання?
Відповіді:
Якщо у вас є лише одна посилання на рядок, і ви об'єднаєте іншу рядок до кінця, 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 = ''" "для i в xrange (10): s + = 'a'" 1000000 петель, найкраще 3: 1,85 Usec на цикл $ python -m timeit -s "s = ''" "для i в xrange (100): s + = 'a'" 10000 петель, найкраще 3: 16,8 Usec за петлю $ python -m timeit -s "s = ''" "для i в xrange (1000): s + = 'a'" 10000 петель, найкраще 3: 158 Usec за петлю $ python -m timeit -s "s = ''" "для i в xrange (10000): s + = 'a'" 1000 петель, найкраще 3: 1,71 мсек за петлю $ python -m timeit -s "s = ''" "для i в xrange (100000): s + = 'a'" 10 петель, найкраще 3: 14,6 мсек за петлю $ python -m timeit -s "s = ''" "для i в xrange (1000000): s + = 'a'" 10 петель, найкраще 3: 173 мсек за петлю
Однак важливо зазначити, що ця оптимізація не є частиною специфікації Python. Наскільки я знаю, це лише в реалізації cPython. Наприклад, те саме емпіричне тестування на pypy або jython може демонструвати старіші показники O (n ** 2).
$ pypy -m timeit -s "s = ''" "для i в xrange (10): s + = 'a'" 10000 петель, найкраще 3: 90,8 Usec за петлю $ pypy -m timeit -s "s = ''" "для i в xrange (100): s + = 'a'" 1000 петель, найкраще 3: 896 Usec на петлю $ pypy -m timeit -s "s = ''" "для i в xrange (1000): s + = 'a'" 100 петель, найкраще 3: 9,03 мсек за петлю $ pypy -m timeit -s "s = ''" "для i в xrange (10000): s + = 'a'" 10 петель, найкраще 3: 89,5 мсек за петлю
Поки що добре, але тоді,
$ pypy -m timeit -s "s = ''" "для i в 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'
Це означає, що якщо ви прагнете щось на зразок StringBuilder Java, канонічна ідіома Python полягає в тому, щоб додати елементи до списку, а потім використати str.join
для об'єднання їх у кінці:
l = []
l.append('foo')
l.append('bar')
l.append('baz')
s = ''.join(l)
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
Це з'єднує str1 і str2 з пробілом як роздільники. Ви також можете зробити "".join(str1, str2, ...)
. str.join()
займає повторення, тому вам доведеться помістити рядки в список або кортеж.
Це приблизно так само ефективно, як і для вбудованого методу.
Не варто.
Тобто, у більшості випадків вам краще генерувати весь рядок за один раз, а не додавати до вже наявного рядка.
Наприклад, не робіть: 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
додаєте текст до нього.
Якщо ви просто додаєте два рядки, тоді просто використовуйте +
.
В основному, різниці немає. Єдина послідовна тенденція полягає в тому, що 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 с на петлю
Пітон 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 с на петлю
Пітон 3.6
1 петля, найкраще з 3: 9,24 с на петлю
1.19 s
і 992 ms
відповідно на Python2.7
додайте рядки з функцією __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)