¿Qué tan lenta es la concatenación de cuerdas de Python frente a str.join?

Como resultado de los comentarios en mi respuesta en este hilo , quería saber cuál es la diferencia de velocidad entre el operador += y ''.join()

Entonces, ¿cuál es la comparación de velocidad entre los dos?

Related of "¿Qué tan lenta es la concatenación de cuerdas de Python frente a str.join?"

De: Concatenación eficiente de cuerdas

Método 1:

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

Método 4:

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

Ahora me doy cuenta de que no son estrictamente representativos, y el cuarto método se agrega a una lista antes de recorrer y unir cada elemento, pero es una indicación justa.

La unión de cuerdas es significativamente más rápida que la concatenación.

¿Por qué? Las cuerdas son inmutables y no se pueden cambiar en su lugar. Para modificar uno, se necesita crear una nueva representación (una concatenación de los dos).

texto alternativo

Mi código original estaba equivocado, parece que la concatenación es generalmente más rápida (especialmente con las versiones más recientes de Python en hardware más nuevo)

Los tiempos son los siguientes:

 Iterations: 1,000,000 

Python 3.3 en 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 en 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 

En Linux Mint, Python 2.7, un procesador más lento

 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 

Y aquí está el código:

 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() 

Las respuestas existentes están muy bien escritas e investigadas, pero aquí hay otra respuesta para la era de Python 3.6, ya que ahora tenemos interpolación de cadenas literal (AKA, f string):

 >>> 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 

Prueba realizada con CPython 3.6.5 en una Retina MacBook Pro 2012 con un Intel Core i7 a 2,3 GHz.

Esto no es de ninguna manera un punto de referencia formal, pero parece que el uso de f string es más o menos eficiente que el uso de += concatenación; Cualquier métrica o sugerencia mejorada son, por supuesto, bienvenidas.

Esto es lo que los progtwigs tontos están diseñados para probar 🙂

Usar más

 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" 

Salida de:

 Time to run Plusser = 1.16350010965 seconds 

Ahora con unirse …

 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" 

Salida de:

 Time to run Joiner = 21.3877386651 seconds 

Entonces, en Python 2.6 en Windows, diría que + es aproximadamente 18 veces más rápido que unirse 🙂

Reescribí la última respuesta, ¿podría compartir su opinión sobre la forma en que lo probé?

 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") 

NOTA: este ejemplo está escrito en Python 3.5, donde range () actúa como el anterior xrange ()

La salida que obtuve:

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

Personalmente prefiero ” .join ([]) a la ‘forma Plusser’ porque es más limpio y más legible.