python-3.x title - Welches ist die bevorzugte Methode zum Verketten eines Strings in Python?





plot (9)


Während etwas veraltet, Code Like a Pythonista: Idiomatische Python empfiehlt join() über + in diesem Abschnitt . PythonSpeedPerformanceTips wie PythonSpeedPerformanceTips in seinem Abschnitt über die Verkettung von Strings mit dem folgenden Disclaimer:

Die Genauigkeit dieses Abschnitts ist in Bezug auf spätere Versionen von Python umstritten. In CPython 2.5 ist die Verkettung von Zeichenfolgen relativ schnell, obwohl dies möglicherweise nicht auch für andere Python-Implementierungen gilt. Siehe ConcatenationTestCode für eine Diskussion.

Da Pythons string nicht geändert werden kann, habe ich mich gefragt, wie man einen String effizienter verketten kann?

Ich kann so schreiben:

s += stringfromelsewhere

oder so:

s = []
s.append(somestring)

later

s = ''.join(s)

Während ich diese Frage schrieb, fand ich einen guten Artikel, der über das Thema sprach.

http://www.skymind.com/~ocrow/python_string/

Aber es ist in Python 2.x., also hat sich die Frage etwas in Python 3 geändert?




Die Verwendung der In-Place-String-Verkettung durch '+' ist die WORST-Methode der Verkettung in Bezug auf Stabilität und Cross-Implementierung, da sie nicht alle Werte unterstützt. Der PEP8-Standard entmutigt dies und regt die Verwendung von format (), join () und append () zur langfristigen Verwendung an.




Mein Anwendungsfall war geringfügig anders. Ich musste eine Abfrage erstellen, wo mehr als 20 Felder dynamisch waren. Ich folgte diesem Ansatz der Verwendung der Formatmethode

query = "insert into {0}({1},{2},{3}) values({4}, {5}, {6})"
query.format('users','name','age','dna','suzan',1010,'nda')

das war für mich vergleichsweise einfacher, anstatt + oder andere Wege zu benutzen







Die empfohlene Methode besteht weiterhin darin, append und join zu verwenden.




Wenn Sie viele Werte verketten, dann auch nicht. Das Anhängen einer Liste ist teuer. Sie können StringIO dafür verwenden. Vor allem, wenn Sie es über viele Operationen hinweg aufbauen.

from cStringIO import StringIO
# python3:  from io import StringIO

buf = StringIO()

buf.write('foo')
buf.write('foo')
buf.write('foo')

buf.getvalue()
# 'foofoofoo'

Wenn Sie bereits eine vollständige Liste von einem anderen Vorgang erhalten haben, verwenden Sie einfach die ''.join(aList)

Aus der Python-FAQ: Was ist der effizienteste Weg, um viele Strings miteinander zu verketten?

str- und bytes-Objekte sind unveränderlich, daher ist das Verketten vieler Strings ineffizient, da jede Verkettung ein neues Objekt erzeugt. Im allgemeinen Fall sind die gesamten Laufzeitkosten quadratisch in der gesamten Stringlänge.

Um viele str-Objekte zu akkumulieren, empfiehlt es sich, sie in eine Liste aufzunehmen und am Ende str.join () aufzurufen:

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)

(Ein anderes vernünftiges Idiom ist io.StringIO)

Um viele Byte-Objekte zu akkumulieren, wird empfohlen, ein Bytearray-Objekt unter Verwendung der direkten Verkettung (der Operator + =) zu erweitern:

result = bytearray()
for b in my_bytes_objects:
    result += b

Edit: Ich war albern und hatte die Ergebnisse rückwärts eingefügt, so dass es aussieht, als wäre das Anhängen an eine Liste schneller als cStringIO. Ich habe auch Tests für Bytearray / Str concat hinzugefügt, sowie eine zweite Runde von Tests mit einer größeren Liste mit größeren Strings. (Python 2.7.3)

ipython-Testbeispiel für große String-Listen

try:
    from cStringIO import StringIO
except:
    from io import StringIO

source = ['foo']*1000

%%timeit buf = StringIO()
for i in source:
    buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop

%%timeit out = []
for i in source:
    out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop

%%timeit out = bytearray()
for i in source:
    out += i
# 10000 loops, best of 3: 98.5 µs per loop

%%timeit out = ""
for i in source:
    out += i
# 10000 loops, best of 3: 161 µs per loop

## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching 
## done by the Python
source = ['foo']*1000

# cStringIO
# 10 loops, best of 3: 19.2 ms per loop

# list append and join
# 100 loops, best of 3: 144 ms per loop

# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop

# str() +=
# 100 loops, best of 3: 5.11 ms per loop



Sie schreiben diese Funktion

def str_join(*args):
    return ''.join(map(str, args))

Dann können Sie einfach anrufen, wo immer Sie wollen

str_join('Pine')  # Returns : Pine
str_join('Pine', 'apple')  # Returns : Pineapple
str_join('Pine', 'apple', 3)  # Returns : Pineapple3



Die beste Methode zum Anhängen einer Zeichenfolge an eine Zeichenfolgenvariable ist die Verwendung von + oder += . Dies liegt daran, dass es lesbar und schnell ist. Sie sind auch genauso schnell, was Sie wählen, ist eine Frage des Geschmacks, der letztere ist der häufigste. Hier sind Timings mit dem Modul timeit :

a = a + b:
0.11338996887207031
a += b:
0.11040496826171875

Diejenigen, die empfehlen, Listen zu haben und an sie anzuhängen und dann diesen Listen beizutreten, tun dies, weil das Anhängen einer Zeichenfolge an eine Liste im Vergleich zur Erweiterung einer Zeichenfolge vermutlich sehr schnell ist. Und das kann in einigen Fällen wahr sein. Hier wird beispielsweise eine Million einer Zeichenfolge mit einem Zeichen angehängt, zuerst in eine Zeichenfolge und dann in eine Liste:

a += b:
0.10780501365661621
a.append(b):
0.1123361587524414

OK, stellt sich heraus, dass selbst wenn die resultierende Zeichenfolge eine Million Zeichen lang ist, das Anhängen noch schneller war.

Versuchen wir nun, eine tausend Zeichen lange Zeichenfolge hunderttausend Mal anzuhängen:

a += b:
0.41823482513427734
a.append(b):
0.010656118392944336

Die End-Zeichenfolge endet daher etwa 100 MB lang. Das war ziemlich langsam, das Anhängen an eine Liste war viel schneller. Dass dieses Timing nicht das finale a.join() . Wie lange würde das dauern?

a.join(a):
0.43739795684814453

Oups. Stellt sich heraus, sogar in diesem Fall ist append / join langsamer.

Woher kommt diese Empfehlung? Python 2?

a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474

Nun, append / join ist dort marginal schneller, wenn Sie extrem lange Strings verwenden (was Sie normalerweise nicht tun, was wäre ein String, der 100 MB im Speicher hat?)

Aber der wahre Drahtzieher ist Python 2.3. Wo ich dir nicht mal die Timings zeigen werde, weil es so langsam ist, dass es noch nicht fertig ist. Diese Tests dauern plötzlich Minuten . Bis auf den Append / Join, der genauso schnell ist wie unter späteren Pythons.

Jep. String-Verkettung war in Python in der Steinzeit sehr langsam. Aber auf 2.4 ist es nicht mehr (oder zumindest Python 2.4.7), daher wurde die Empfehlung, append / join zu verwenden, 2008 veraltet, als Python 2.3 nicht mehr aktualisiert wurde, und Sie sollten es nicht mehr verwenden. :-)

(Update: Es stellt sich heraus, dass ich beim Testen vorsichtiger war, dass die Verwendung von + und += für zwei Strings auf Python 2.3 schneller ist. Die Empfehlung ''.join() muss ein Missverständnis sein)

Dies ist jedoch CPython. Andere Implementierungen können andere Bedenken haben. Und dies ist nur ein weiterer Grund, warum vorzeitige Optimierung die Wurzel allen Übels ist. Verwenden Sie keine Technik, die "schneller" ist, es sei denn, Sie messen sie zuerst.

Daher ist die "beste" Version für die String-Verkettung die Verwendung von + oder + = . Und wenn das für Sie langsam ist, was ziemlich unwahrscheinlich ist, dann machen Sie etwas anderes.

Also, warum verwende ich viel append / join in meinem Code? Denn manchmal ist es tatsächlich klarer. Besonders wenn alles, was Sie verketten sollten, durch Leerzeichen oder Kommas oder Zeilenumbrüche getrennt sein sollte.




Hier ist meine Java 8- basierte Lösung, die die neue Stream-API verwendet , um alle Zeilen von einem zu sammeln InputStream:

public static String toString(InputStream inputStream) {
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(inputStream));
    return reader.lines().collect(Collectors.joining(
        System.getProperty("line.separator")));
}




python string python-3.x concat