Как добавить одну строку в другую в Python?

Я хочу эффективный способ добавления одной строки к другой в Python.

var1 = "foo"
var2 = "bar"
var3 = var1 + var2

есть ли хороший встроенный метод?

9 ответов


если у вас есть только одна ссылка на строку, и вы объединяете другую строку до конца, 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  byte (newsize
   does *not* include that), and a trailing  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] = '';
    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

это важно!--19--> однако следует отметить, что эта оптимизация не является частью спецификации 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 делает то, что хорошо работает с короткими строками, но плохо работает для больших строк.


не оптимизируйте преждевременно. Если у вас нет причин полагать, что есть узкое место скорости, вызванное конкатенациями строк, просто придерживайтесь + и +=:

s  = 'foo'
s += 'bar'
s += 'baz'

тем не менее, если вы стремитесь к чему-то вроде StringBuilder Java, каноническая идиома 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() принимает iterable, поэтому вам придется поместить строки в список или кортеж.

Это примерно так же эффективно, как и для встроенного метода.


это действительно зависит от вашего приложения. Если вы перебираете сотни слов и хотите добавить их все в список,.join() лучше. Но если вы собираете длинное предложение, вам лучше использовать +=.


Если вам нужно сделать много операций добавления для создания большой строки, вы можете использовать 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 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 s в цикле

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__ функции

str = "Hello"
str2 = " World"
st = str.__add__(str2)
print(st)

выход

Hello World