wake-up-neo.com

Wie hänge ich in Python eine Zeichenfolge an eine andere an?

In Python möchte ich auf effiziente Weise eine Zeichenfolge an eine andere anhängen.

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

Gibt es eine gute eingebaute Methode zu verwenden?

556
user469652

Wenn Sie nur einen Verweis auf eine Zeichenfolge haben und eine andere Zeichenfolge bis zum Ende verketten, macht CPython dies jetzt in besonderen Fällen und versucht, die Zeichenfolge an Ort und Stelle zu erweitern.

Das Endergebnis ist, dass die Operation O (n) abgeschrieben wird.

z.B.

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

früher war es O (n ^ 2), jetzt ist es O (n).

Aus der Quelle (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;
}

Es ist leicht genug, empirisch zu verifizieren.

 $ python -m timeit -s "s = ''" "für i in xrange (10): s + = 'a'" 
 1000000 Schleifen, am besten von 3 : 1,85 usec pro Schleife 
 $ python -m timeit -s "s = ''" "für i in xrange (100): s + = 'a'" 
 10000 Schleifen, am besten 3: 16,8 usec pro Schleife 
 $ python -m timeit -s "s = ''" "für i in xrange (1000): s + = 'a'" 
 10000 Schleifen, am besten 3: 158 usec pro Schleife 
 $ python -m timeit -s "s = ''" "für i in xrange (10000): s + = 'a' "
 1000 Schleifen, am besten 3: 1,71 ms pro Schleife 
 $ python -m timeit -s" s = '' "" für i in xrange ( 100000): s + = 'a' "
 10 Schleifen, am besten 3: 14,6 ms pro Schleife 
 $ python -m timeit -s" s = '' "" für i im x-Bereich (1000000): s + = 'a' "
 10 Schleifen, am besten 3: 173 ms pro Schleife 

Es ist wichtig Beachten Sie jedoch, dass diese Optimierung nicht Teil der Python -Spezifikation ist. Soweit ich weiß, ist es nur in der cPython-Implementierung. Dieselben empirischen Tests mit Pypy oder Jython könnten beispielsweise die ältere O (n ** 2) -Leistung zeigen.

 $ pypy -m timeit -s "s = ''" "für i in xrange (10): s + = 'a'" 
 10000 Schleifen, am besten 3: 90,8 usec pro Schleife 
 $ pypy -m timeit -s "s = ''" "für i in xrange (100): s + = 'a'" 
 1000 Schleifen, am besten 3: 896 usec pro Schleife 
 $ pypy -m timeit -s "s = ''" "für i in xrange (1000): s + = 'a'" 
 100 Schleifen, am besten 3: 9,03 ms pro Schleife 
 $ pypy -m timeit -s "s = ''" "für i in xrange (10000): s + = 'a'" 
 10 Schleifen, am besten 3: 89,5 ms pro Schleife 

So weit so gut, aber dann

 $ pypy -m timeit -s "s = ''" "für i in xrange (100000): s + = 'a'" 
 10 Schleifen, am besten 3: 12,8 Sekunden pro Schleife 

autsch noch schlimmer als quadratisch. Pypy macht also etwas, das mit kurzen Saiten gut funktioniert, aber für größere Saiten schlecht funktioniert.

573
John La Rooy

Nicht vorzeitig optimieren. Wenn Sie keinen Grund zur Annahme haben, dass es einen Geschwindigkeitsengpass gibt, der durch Zeichenfolgenverkettungen verursacht wird, bleiben Sie einfach bei + und +=:

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

Das heißt, wenn Sie auf etwas wie Javas StringBuilder abzielen, besteht das kanonische Python-Idiom darin, Elemente zu einer Liste hinzuzufügen und dann str.join zu verwenden, um sie alle am Ende zu verketten:

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

s = ''.join(l)
272
John Kugelman

Nicht.

Das heißt, in den meisten Fällen ist es besser, die gesamte Zeichenfolge auf einmal zu generieren, als an eine vorhandene Zeichenfolge anzuhängen.

Zum Beispiel nicht tun: obj1.name + ":" + str(obj1.count)

Stattdessen: benutze "%s:%d" % (obj1.name, obj1.count)

Das wird einfacher zu lesen und effizienter.

38
Winston Ewert
str1 = "Hello"
str2 = "World"
newstr = " ".join((str1, str2))

Das verbindet str1 und str2 mit einem Leerzeichen als Trennzeichen. Sie können auch "".join(str1, str2, ...) ausführen. str.join() nimmt einen iterablen Wert an, sodass Sie die Zeichenfolgen in eine Liste oder ein Tupel einfügen müssen.

Das ist ungefähr so ​​effizient wie bei einer eingebauten Methode.

36
Rafe Kettler

Wenn Sie viele Anhängeoperationen ausführen müssen, um eine große Zeichenfolge zu erstellen, können Sie StringIO oder cStringIO verwenden. Die Schnittstelle ist wie eine Datei. dh: Sie write, um Text daran anzuhängen.

Wenn Sie nur zwei Zeichenfolgen anhängen, verwenden Sie einfach +.

10

es hängt wirklich von Ihrer Anwendung ab. Wenn Sie Hunderte von Wörtern durchlaufen und sie alle in einer Liste anhängen möchten, ist .join() besser. Aber wenn Sie einen langen Satz zusammensetzen, ist es besser, += zu verwenden.

9
Ramy

Python 3.6 gibt uns f-Strings , die eine Freude sind:

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar

In den geschweiften Klammern können Sie fast alles machen

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2
7
Trenton

Grundsätzlich kein Unterschied. Der einzig beständige Trend ist, dass Python mit jeder Version langsamer wird ... :(


Liste

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

Python 2.7

1 Schleife, am besten 3: 7.34 s pro Schleife

Python 3.4

1 Schleife, am besten 3: 7,99 s pro Schleife

Python 3.5

1 Schleife, am besten 3: 8,48 s pro Schleife

Python 3.6

1 Schleife, am besten 3: 9,9 s pro Schleife


String

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

Python 2.7:

1 Schleife, am besten 3: 7,41 s pro Schleife

Python 3.4

1 Schleife, am besten 3: 9.08 s pro Schleife

Python 3.5

1 Schleife, am besten 3: 8,82 s pro Schleife

Python 3.6

1 Schleife, am besten 3: 9.24 s pro Schleife

5
ostrokach
a='foo'
b='baaz'

a.__add__(b)

out: 'foobaaz'
3

fügen Sie Zeichenfolgen mit der Funktion _ ADD _ hinzu

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

Ausgabe

Hello World
3
saigopi