Wie langsam ist Pythons String-Verkettung im Vergleich zu str.join?

Als Ergebnis der Kommentare in meiner Antwort zu diesem Thread wollte ich wissen, was der Geschwindigkeitsunterschied zwischen dem += Operator und ''.join()

Was ist der Geschwindigkeitsvergleich zwischen den beiden?

   

Von: Effiziente String-Verkettung

Methode 1:

 def method1(): out_str = '' for num in xrange(loop_count): out_str += 'num' return out_str 

Methode 4:

 def method4(): str_list = [] for num in xrange(loop_count): str_list.append('num') return ''.join(str_list) 

Jetzt erkenne ich, dass sie nicht streng repräsentativ sind, und die vierte Methode hängt an eine Liste an, bevor sie durch jeden einzelnen Punkt hindurchgeht und ihn verbindet, aber es ist ein fairer Hinweis.

String Join ist wesentlich schneller als Verkettung.

Warum? Strings sind unveränderlich und können nicht geändert werden. Um eine zu ändern, muss eine neue Repräsentation erstellt werden (eine Verkettung der beiden).

Alt-Text

Mein ursprünglicher Code war falsch, es scheint, dass + Verkettung normalerweise schneller ist (besonders mit neueren Versionen von Python auf neuerer Hardware)

Die Zeiten sind wie folgt:

 Iterations: 1,000,000 

Python 3.3 unter Windows 7, Core i7

 String of len: 1 took: 0.5710 0.2880 seconds String of len: 4 took: 0.9480 0.5830 seconds String of len: 6 took: 1.2770 0.8130 seconds String of len: 12 took: 2.0610 1.5930 seconds String of len: 80 took: 10.5140 37.8590 seconds String of len: 222 took: 27.3400 134.7440 seconds String of len: 443 took: 52.9640 170.6440 seconds 

Python 2.7 unter Windows 7, Core i7

 String of len: 1 took: 0.7190 0.4960 seconds String of len: 4 took: 1.0660 0.6920 seconds String of len: 6 took: 1.3300 0.8560 seconds String of len: 12 took: 1.9980 1.5330 seconds String of len: 80 took: 9.0520 25.7190 seconds String of len: 222 took: 23.1620 71.3620 seconds String of len: 443 took: 44.3620 117.1510 seconds 

Auf Linux Mint, Python 2.7, etwas langsamer processor

 String of len: 1 took: 1.8840 1.2990 seconds String of len: 4 took: 2.8394 1.9663 seconds String of len: 6 took: 3.5177 2.4162 seconds String of len: 12 took: 5.5456 4.1695 seconds String of len: 80 took: 27.8813 19.2180 seconds String of len: 222 took: 69.5679 55.7790 seconds String of len: 443 took: 135.6101 153.8212 seconds 

Und hier ist der Code:

 from __future__ import print_function import time def strcat(string): newstr = '' for char in string: newstr += char return newstr def listcat(string): chars = [] for char in string: chars.append(char) return ''.join(chars) def test(fn, times, *args): start = time.time() for x in range(times): fn(*args) return "{:>10.4f}".format(time.time() - start) def testall(): strings = ['a', 'long', 'longer', 'a bit longer', '''adjkrsn widn fskejwoskemwkoskdfisdfasdfjiz oijewf sdkjjka dsf sdk siasjk dfwijs''', '''this is a really long string that's so long it had to be triple quoted and contains lots of superflous characters for kicks and gigles @!#(*_#)(*$(*!#@&)(*E\xc4\x32\xff\x92\x23\xDF\xDFk^%#$!)%#^(*#''', '''I needed another long string but this one won't have any new lines or crazy characters in it, I'm just going to type normal characters that I would usually write blah blah blah blah this is some more text hey cool what's crazy is that it looks that the str += is really close to the O(n^2) worst case performance, but it looks more like the other method increases in a perhaps linear scale? I don't know but I think this is enough text I hope.'''] for string in strings: print("String of len:", len(string), "took:", test(listcat, 1000000, string), test(strcat, 1000000, string), "seconds") testall() 

Die vorhandenen Antworten sind sehr gut geschrieben und recherchiert, aber hier ist eine andere Antwort für die Python 3.6-Ära, denn jetzt haben wir literale String-Interpolation (AKA, f Strings):

 >>> import timeit >>> timeit.timeit('f\'{"a"}{"b"}{"c"}\'', number=1000000) 0.14618930302094668 >>> timeit.timeit('"".join(["a", "b", "c"])', number=1000000) 0.23334730707574636 >>> timeit.timeit('a = "a"; a += "b"; a += "c"', number=1000000) 0.14985873899422586 

Test durchgeführt mit CPython 3.6.5 auf einem 2012 Retina MacBook Pro mit einem Intel Core i7 bei 2,3 GHz.

Dies ist keineswegs ein formeller Maßstab, aber es sieht so aus, als wäre die Verwendung von f Strings ungefähr so ​​performant wie die Verwendung von += Verkettung; Verbesserte Metriken oder Vorschläge sind natürlich willkommen.

Dies ist, was dumme Programme zu testen sind 🙂

Verwenden Sie plus

 import time if __name__ == '__main__': start = time.clock() for x in range (1, 10000000): dog = "a" + "b" end = time.clock() print "Time to run Plusser = ", end - start, "seconds" 

Ausgabe von:

 Time to run Plusser = 1.16350010965 seconds 

Jetzt mit beitreten ….

 import time if __name__ == '__main__': start = time.clock() for x in range (1, 10000000): dog = "a".join("b") end = time.clock() print "Time to run Joiner = ", end - start, "seconds" 

Ausgabe von:

 Time to run Joiner = 21.3877386651 seconds 

Also auf Python 2.6 auf Windows würde ich sagen, + ist etwa 18 mal schneller als beitreten 🙂

Ich habe die letzte Antwort umgeschrieben. Könnten Sie bitte Ihre Meinung dazu mitteilen, wie ich getestet habe?

 import time start1 = time.clock() for x in range (10000000): dog1 = ' and '.join(['spam', 'eggs', 'spam', 'spam', 'eggs', 'spam','spam', 'eggs', 'spam', 'spam', 'eggs', 'spam']) end1 = time.clock() print("Time to run Joiner = ", end1 - start1, "seconds") start2 = time.clock() for x in range (10000000): dog2 = 'spam'+' and '+'eggs'+' and '+'spam'+' and '+'spam'+' and '+'eggs'+' and '+'spam'+' and '+'spam'+' and '+'eggs'+' and '+'spam'+' and '+'spam'+' and '+'eggs'+' and '+'spam' end2 = time.clock() print("Time to run + = ", end2 - start2, "seconds") 

HINWEIS: Dieses Beispiel ist in Python 3.5 geschrieben, wo range () wie der vorherige xrange () funktioniert

Der Ausgang, den ich bekam:

 Time to run Joiner = 27.086106206103153 seconds Time to run + = 69.79100515996426 seconds 

Persönlich bevorzuge ich “.join ([]) über den ‘Plusser Weg’, weil es sauberer und lesbarer ist.