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?
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 loopNa 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.
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)
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)
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.
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 +=
.
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 +
.
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ę
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'
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
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