Я хочу эффективный способ добавить одну строку в другую в Python.Как добавить одну строку в другую в Python?
var1 = "foo"
var2 = "bar"
var3 = var1 + var2
Есть ли какой-либо хороший встроенный метод для использования?
Я хочу эффективный способ добавить одну строку в другую в Python.Как добавить одну строку в другую в 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=''" "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
Уч даже хуже, чем квадратичная. Таким образом, pypy делает что-то, что хорошо работает с короткими строками, но плохо работает для больших строк.
Если вам нужно сделать много операций добавления для создания большой строки, вы можете использовать StringIO или cStringIO. Интерфейс подобен файлу. т.е.: write
, чтобы добавить текст к нему.
Если вы просто добавляете две строки, просто используйте +
.
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))
Это соединяет str1 и str2 с пространством в качестве разделителей. Вы также можете сделать "".join(str1, str2, ...)
. str.join()
выполняет итерацию, поэтому вам нужно будет поместить строки в список или кортеж.
Это примерно так же эффективно, как и для встроенного метода.
Не следует преждевременно оптимизировать. Если у вас нет никаких оснований полагать, что есть скорость узкого места, вызванные строковыми сцепления, то просто придерживаться +
и +=
:
s = 'foo'
s += 'bar'
s += 'baz'
Это сказало, если вы стремитесь к чему-то вроде StringBuilder в Java канонического Python идиома является добавлять элементы в список, а затем использовать str.join
, чтобы объединить их все в конце:
l = []
l.append('foo')
l.append('bar')
l.append('baz')
s = ''.join(l)
Я не знаю, какие последствия скорости построения ваших строк в списках, а затем. join() их, но я считаю, что это самый чистый способ. У меня также были большие успехи с использованием нотации% s в строке для SQL-шаблона, который я написал. – richo
@ Richo Использование .join более эффективно. Причина в том, что строки Python неизменяемы, поэтому многократно используя s + = more будет выделять множество последовательно больших строк. .join будет генерировать конечную строку за один проход от ее составных частей. – Ben
@Ben, было значительное улучшение в этой области - см. Мой ответ –
не делать.
То есть, в большинстве случаев вам лучше генерировать целую строку за один раз, а не добавлять существующую строку.
Например, не делают: obj1.name + ":" + str(obj1.count)
Вместо: используйте "%s:%d" % (obj1.name, obj1.count)
Это будет легче читать и более эффективным.
Прошу прощения, нет ничего проще читать, чем (строка + строка), как первый пример, второй пример может быть более эффективным, но не более читаемый – ExceptionSlayer
@ExceptionSlayer, строка + строка довольно проста. Но ''
Это совсем не помогает, m пытается сделать грубый эквивалент, скажем, «string. = verifydata()« PHP/perl »или аналогичный. – Shadur
Это действительно зависит от вашего приложения. Если вы перебираете сотни слов и хотите добавить их в список, лучше - .join()
. Но если вы собрали длинное предложение, вам лучше использовать +=
.
В принципе, никакой разницы. Единственная стойкая тенденция в том, что 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 с на петле
Python 3,5
1 цикл, лучше всего из 3: 8,48 s за петлю
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 s в петле
Я думаю, это зависит. Я получаю '1.19 s' и' 992 ms' соответственно на Python2.7 –
@JohnLaRooy Да, вы правы. Я отредактировал ответ. – ostrokach
a='foo'
b='baaz'
a.__add__(b)
out: 'foobaaz'
Код хороший, но это поможет получить сопроводительное объяснение. Зачем использовать этот метод, а не другие ответы на этой странице? – cgmb
Использование 'a .__ add __ (b)' идентично написанию 'a + b'. Когда вы объединяете строки, используя оператор '+', Python будет вызывать метод '__add__' в строке слева, передавая правую боковую строку в качестве параметра. – Addie
Интересно. Под «сейчас», вы имеете в виду Python 3.x? –
@Steve, No. Это, по крайней мере, в 2.6, возможно, даже 2.5 –
Вы указали функцию 'PyString_ConcatAndDel', но включили комментарий для' _PyString_Resize'. Кроме того, комментарий действительно не устанавливает ваше требование относительно Big-O –