Jak dodać jeden ciąg znaków do drugiego w Pythonie?

Chcę wydajnego sposobu dodawania jednego ciągu do drugiego w Pythonie.

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

Czy jest jakaś dobra wbudowana metoda?

Author: Steven Vascellaro, 2010-12-14

9 answers

Jeśli masz tylko jedno odniesienie do łańcucha i połączysz inny łańcuch do końca, CPython teraz specjalne przypadki to i próbuje rozszerzyć łańcuch w miejscu.

Wynikiem końcowym jest to, że operacja jest amortyzowana O (n).

Np.

s = ""
for i in range(n):
    s+=str(i)

Kiedyś było O (N^2), ale teraz jest O (n).

Ze źródła (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;
}
Łatwo to sprawdzić empirycznie.
$ 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

Ważne jest jednak, aby pamiętać, że ta optymalizacja nie jest częścią specyfikacji Pythona. Z tego co wiem to tylko w implementacji cpythona. Te same badania empiryczne na przykład pypy lub jython mogą pokazać starszą wydajność 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
Na razie dobrze, ale potem ... ]}
$ pypy -m timeit -s"s=''" "for i in xrange(100000):s+='a'"
10 loops, best of 3: 12.8 sec per loop
/ Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center / Więc pypy robi coś, co działa dobrze z krótkimi strunami, ale wykonuje słabo dla większych strun.
 493
Author: John La Rooy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-19 01:00:50

Nie Optymalizuj przedwcześnie. Jeśli nie masz powodu, aby wierzyć, że istnieje wąskie gardło prędkości spowodowane konkatenacjami łańcuchów, po prostu trzymaj się + i +=:

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

To powiedziawszy, jeśli dążysz do czegoś takiego jak Java ' S StringBuilder, kanonicznym idiomem Pythona jest dodawanie elementów do listy, a następnie używanie str.join, Aby połączyć je wszystkie na końcu:

l = []
l.append('foo')
l.append('bar')
l.append('baz')

s = ''.join(l)
 239
Author: John Kugelman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-14 01:45:11

Nie.]}

Oznacza to, że w większości przypadków lepiej jest wygenerować cały ciąg za jednym zamachem, niż dołączyć do istniejącego łańcucha.

Na przykład nie rób: obj1.name + ":" + str(obj1.count)

Zamiast: użyj "%s:%d" % (obj1.name, obj1.count)

To będzie łatwiejsze do odczytania i bardziej efektywne.
 34
Author: Winston Ewert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-14 02:06:59
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

Który łączy str1 i str2 ze spacją jako separatory. Możesz również zrobić "".join(str1, str2, ...). str.join() bierze iterowalny, więc musisz umieścić ciągi na liście lub krotce.

To mniej więcej tak skuteczne, jak w przypadku metody wbudowanej.

 27
Author: Rafe Kettler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-14 01:42:55

To naprawdę zależy od twojej aplikacji. Jeśli zapętlasz setki słów i chcesz dodać je wszystkie do listy, .join() jest lepszy. Ale jeśli składasz długie zdanie, lepiej Użyj +=.

 9
Author: Ramy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-26 15:57:21

Jeśli potrzebujesz wykonać wiele operacji dołączania, aby zbudować duży ciąg znaków, możesz użyć StringIO lub cStringIO. Interfejs jest jak plik. ie: you write to append text to it.

Jeśli dodajesz tylko dwa łańcuchy, użyj +.

 8
Author: Laurence Gonsalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-30 20:31:52

Zasadniczo, bez różnicy. Jedynym stałym trendem jest to, że Python wydaje się być coraz wolniejszy z każdą wersją... :(


Lista

%%timeit
x = []
for i in range(100000000):  # xrange on Python 2.7
    x.append('a')
x = ''.join(x)

Python 2.7

1 loop, best of 3: 7.34 s na pętlę

Python 3.4

1 loop, best of 3: 7.99 s na pętlę

Python 3.5

1 loop, best of 3: 8.48 s na pętlę

Python 3.6

1 loop, best of 3: 9.93 s na pętlę


String

%%timeit
x = ''
for i in range(100000000):  # xrange on Python 2.7
    x += 'a'

Python 2.7:

1 pętla, best of 3: 7.41 s na pętlę

Python 3.4

1 loop, best of 3: 9.08 s na pętlę

Python 3.5

1 loop, best of 3: 8.82 s na pętlę

Python 3.6

1 loop, best of 3: 9.24 s na pętlę

 4
Author: ostrokach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 18:14:16
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'
 3
Author: Rahul shrivastava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-20 17:42:30

Dodawanie łańcuchów za pomocą __dodaj__ Funkcja

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

Wyjście

Hello World
 0
Author: saigopi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-18 12:21:46