Мне нужен эффективный способ добавления одной строки к другой в 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 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 loops, best of 3: 1.85 usec per loop $ python -m timeit -s"s=''" "for i in xrange(100):s+='a'" 10000 loops, best of 3: 16.8 usec per loop $ python -m timeit -s"s=''" "for i in xrange(1000):s+='a'" 10000 loops, best of 3: 158 usec per loop $ python -m timeit -s"s=''" "for i in xrange(10000):s+='a'" 1000 loops, best of 3: 1.71 msec per loop $ python -m timeit -s"s=''" "for i in xrange(100000):s+='a'" 10 loops, best of 3: 14.6 msec per loop $ python -m timeit -s"s=''" "for i in xrange(1000000):s+='a'" 10 loops, best of 3: 173 msec per loop
Важно, чтобы отметить, что эта оптимизация не является частью спецификации Python. Насколько мне известно, это только в реализации cPython. Те же эмпирические тесты на pypy или jython, например, могут показать более высокую производительность O (n ** 2).
$ pypy -m timeit -s"s=''" "for i in xrange(10):s+='a'" 10000 loops, best of 3: 90.8 usec per loop $ pypy -m timeit -s"s=''" "for i in xrange(100):s+='a'" 1000 loops, best of 3: 896 usec per loop $ pypy -m timeit -s"s=''" "for i in xrange(1000):s+='a'" 100 loops, best of 3: 9.03 msec per loop $ pypy -m timeit -s"s=''" "for i in xrange(10000):s+='a'" 10 loops, best of 3: 89.5 msec per loop
До сих пор так хорошо, но потом,
$ pypy -m timeit -s"s=''" "for i in xrange(100000):s+='a'" 10 loops, best of 3: 12.8 sec per loop
ouch даже хуже, чем квадратичный. Таким образом, 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()
принимает итерацию, поэтому вам нужно будет поместить строки в список или кортеж.
Это примерно так же эффективно, как и для встроенного метода.
Если вам нужно выполнить много операций добавления для создания большой строки, вы можете использовать StringIO или cStringIO. Интерфейс похож на файл. то есть: вы write
, чтобы добавить текст к нему.
Если вы просто добавляете две строки, просто используйте +
.
это действительно зависит от вашего приложения. Если вы перебираете сотни слов и хотите добавить их в список, лучше .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
В принципе, никакой разницы. Единственная постоянная тенденция заключается в том, что 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 s за цикл
Python 3.4
1, лучше всего 3: 7.99 s за цикл
Python 3.5
1, лучше всего 3: 8.48 s за цикл
Python 3.6
1, лучше всего 3: 9.93 s за цикл
%%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 s за цикл
Python 3.5
1, лучше всего 3: 8.82 s за цикл
Python 3.6
1, лучше всего 3: 9.24 s за цикл
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
добавлять строки с помощью функции __add__ function
str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)
Выход
Hello World