una separar reemplazar por lista eliminar contar concatenar comparar como caracteres caracter cadenas cadena python string

separar - ¿Cuál es el método de concatenación de cadenas más eficiente en python?



reemplazar caracteres en python (12)

¿Hay algún método eficaz de concatenación de cadenas masivas en Python (como StringBuilder en C # o StringBuffer en Java)? Encontré los siguientes métodos here :

  • Concatenación simple usando +
  • Usar la lista de cadenas y el método de join
  • Uso de UserString del módulo MutableString
  • Usando la matriz de caracteres y el módulo de array
  • Usando cStringIO desde el módulo StringIO

¿Pero qué usan o sugieren los expertos, y por qué?

[ Una pregunta relacionada aquí ]


Depende de lo que estés haciendo.

Después de Python 2.5, la concatenación de cadenas con el operador + es bastante rápida. Si solo está concatenando un par de valores, usar el operador + funciona mejor:

>>> x = timeit.Timer(stmt="''a'' + ''b''") >>> x.timeit() 0.039999961853027344 >>> x = timeit.Timer(stmt="''''.join([''a'', ''b''])") >>> x.timeit() 0.76200008392333984

Sin embargo, si estás armando una cadena en un bucle, es mejor que utilices el método de unión de listas:

>>> join_stmt = """ ... joined_str = '''' ... for i in xrange(100000): ... joined_str += str(i) ... """ >>> x = timeit.Timer(join_stmt) >>> x.timeit(100) 13.278000116348267 >>> list_stmt = """ ... str_list = [] ... for i in xrange(100000): ... str_list.append(str(i)) ... ''''.join(str_list) ... """ >>> x = timeit.Timer(list_stmt) >>> x.timeit(100) 12.401000022888184

... pero tenga en cuenta que debe armar un número relativamente alto de cadenas antes de que la diferencia se note.


Inspirado en los benchmarks de @JasonBaker, aquí hay uno simple que compara 10 cadenas "abcdefghijklmnopqrstuvxyz" , que muestra que .join() es más rápido; incluso con este pequeño aumento en las variables:

Cadena

>>> x = timeit.Timer(stmt=''"abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz"'') >>> x.timeit() 0.9828147209324385

Unirse

>>> x = timeit.Timer(stmt=''"".join(["abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz"])'') >>> x.timeit() 0.6114138159765048


Me encontré con una situación en la que necesitaba una cadena apilable de tamaño desconocido. Estos son los resultados de referencia (Python 2.7.3):

$ python -m timeit -s ''s=""'' ''s+="a"'' 10000000 loops, best of 3: 0.176 usec per loop $ python -m timeit -s ''s=[]'' ''s.append("a")'' 10000000 loops, best of 3: 0.196 usec per loop $ python -m timeit -s ''s=""'' ''s="".join((s,"a"))'' 100000 loops, best of 3: 16.9 usec per loop $ python -m timeit -s ''s=""'' ''s="%s%s"%(s,"a")'' 100000 loops, best of 3: 19.4 usec per loop

Esto parece mostrar que ''+ ='' es el más rápido. Los resultados del enlace skymind están un poco desactualizados.

(Me doy cuenta de que el segundo ejemplo no está completo, la lista final debería estar unida. Esto muestra, sin embargo, que simplemente la preparación de la lista lleva más tiempo que la cadena concat).


Para un pequeño conjunto de cadenas cortas (es decir, 2 o 3 cadenas de no más de unos pocos caracteres), plus es mucho más rápido. Usando la maravillosa secuencia de comandos de mkoistinen en Python 2 y 3:

plus 2.679107467004 (100.00% as fast) join 3.653773699996 (73.32% as fast) form 6.594011374000 (40.63% as fast) intp 4.568015249999 (58.65% as fast)

Entonces, cuando su código está haciendo una gran cantidad de pequeñas concatenaciones separadas, además es la forma preferida si la velocidad es crucial.


Probablemente, "las nuevas cadenas de caracteres en Python 3.6" es la forma más eficiente de concatenar cadenas.

Usando% s

>>> timeit.timeit("""name = "Some" ... age = 100 ... ''%s is %s.'' % (name, age)""", number = 10000) 0.0029734770068898797

Usando .format

>>> timeit.timeit("""name = "Some" ... age = 100 ... ''{} is {}.''.format(name, age)""", number = 10000) 0.004015227983472869

Usando f

>>> timeit.timeit("""name = "Some" ... age = 100 ... f''{name} is {age}.''""", number = 10000) 0.0019175919878762215

Fuente: https://realpython.com/python-f-strings/


Puede que te interese esto: una anécdota de optimización de Guido. Aunque vale la pena recordar también que este es un artículo antiguo y es anterior a la existencia de cosas como ''''.join (aunque supongo que string.joinfields es más o menos lo mismo)

Sobre la base de eso, el módulo de array puede ser más rápido si puede calzar su problema en él. Pero ''''.join es probablemente lo suficientemente rápido y tiene el beneficio de ser idiomático y, por lo tanto, más fácil de entender para otros programadores de Python.

Finalmente, la regla de oro de la optimización: no optimice a menos que sepa que lo necesita, y mida en lugar de adivinar.

Puede medir diferentes métodos usando el módulo timeit . Eso puede decirte cuál es el más rápido, en lugar de extraños al azar en Internet haciendo conjeturas.


Python 3.6 cambió el juego para la concatenación de cadenas de componentes conocidos con Literal String Interpolation .

Dado el caso de prueba de la respuesta de mkoistinen , tener cuerdas

domain = ''some_really_long_example.com'' lang = ''en'' path = ''some/really/long/path/''

Los contendientes son

  • f''http://{domain}/{lang}/{path}'' - 0.151 μs

  • ''http://%s/%s/%s'' % (domain, lang, path) - 0.321 μs

  • ''http://'' + domain + ''/'' + lang + ''/'' + path - 0.356 μs

  • ''''.join((''http://'', domain, ''/'', lang, ''/'', path)) - 0.249 μs (nótese que construir una tupla de longitud constante es ligeramente más rápido que crear una lista constante).

Por lo tanto, actualmente el código más corto y más hermoso posible también es el más rápido.

En las versiones alfa de Python 3.6, la implementación para cadenas f'''' era la más lenta posible; de ​​hecho, el código de bytes generado es prácticamente equivalente al caso ''''.join() con llamadas innecesarias a str.__format__ que sin argumentos simplemente se devolvería sin cambios . Estas ineficiencias se abordaron antes de 3.6 final.

La velocidad puede contrastarse con el método más rápido para Python 2, que es + concatenación en mi computadora; y eso toma 0.203 μs con cadenas de 8 bits, y 0.259 μs si todas las cadenas son Unicode.


Según la respuesta de John Fouhy, no optimices a menos que tengas que hacerlo, pero si estás aquí y haces esta pregunta, puede ser precisamente porque tienes que hacerlo . En mi caso, necesitaba ensamblar algunas URL de variables de cadena ... rápido. Noté que nadie (hasta ahora) parece estar considerando el método de formato de cadena, así que pensé en probarlo y, sobre todo por poco interés, pensé en lanzar el operador de interpolación de cuerdas allí para medirlo. Para ser honesto, no creía que ninguno de estos se acumulara en una operación directa ''+'' o un '''' .join (). ¿Pero adivina que? En mi sistema Python 2.7.5, el operador de interpolación de cadenas los gobierna a todos y string.format () es el de peor rendimiento:

# concatenate_test.py from __future__ import print_function import timeit domain = ''some_really_long_example.com'' lang = ''en'' path = ''some/really/long/path/'' iterations = 1000000 def meth_plus(): ''''''Using + operator'''''' return ''http://'' + domain + ''/'' + lang + ''/'' + path def meth_join(): ''''''Using ''''.join()'''''' return ''''.join([''http://'', domain, ''/'', lang, ''/'', path]) def meth_form(): ''''''Using string.format'''''' return ''http://{0}/{1}/{2}''.format(domain, lang, path) def meth_intp(): ''''''Using string interpolation'''''' return ''http://%s/%s/%s'' % (domain, lang, path) plus = timeit.Timer(stmt="meth_plus()", setup="from __main__ import meth_plus") join = timeit.Timer(stmt="meth_join()", setup="from __main__ import meth_join") form = timeit.Timer(stmt="meth_form()", setup="from __main__ import meth_form") intp = timeit.Timer(stmt="meth_intp()", setup="from __main__ import meth_intp") plus.val = plus.timeit(iterations) join.val = join.timeit(iterations) form.val = form.timeit(iterations) intp.val = intp.timeit(iterations) min_val = min([plus.val, join.val, form.val, intp.val]) print(''plus %0.12f (%0.2f%% as fast)'' % (plus.val, (100 * min_val / plus.val), )) print(''join %0.12f (%0.2f%% as fast)'' % (join.val, (100 * min_val / join.val), )) print(''form %0.12f (%0.2f%% as fast)'' % (form.val, (100 * min_val / form.val), )) print(''intp %0.12f (%0.2f%% as fast)'' % (intp.val, (100 * min_val / intp.val), ))

Los resultados:

# python2.7 concatenate_test.py plus 0.360787868500 (90.81% as fast) join 0.452811956406 (72.36% as fast) form 0.502608060837 (65.19% as fast) intp 0.327636957169 (100.00% as fast)

Si uso un dominio más corto y un camino más corto, la interpolación aún gana. La diferencia es más pronunciada, sin embargo, con cadenas más largas.

Ahora que tenía un buen guión de prueba, también probé en Python 2.6, 3.3 y 3.4, aquí están los resultados. En Python 2.6, el operador plus es el más rápido. En Python 3, join gana. Nota: estas pruebas son muy repetibles en mi sistema. Entonces, ''más'' siempre es más rápido en 2.6, ''intp'' siempre es más rápido en 2.7 y ''join'' siempre es más rápido en Python 3.x.

# python2.6 concatenate_test.py plus 0.338213920593 (100.00% as fast) join 0.427221059799 (79.17% as fast) form 0.515371084213 (65.63% as fast) intp 0.378169059753 (89.43% as fast) # python3.3 concatenate_test.py plus 0.409130576998 (89.20% as fast) join 0.364938726001 (100.00% as fast) form 0.621366866995 (58.73% as fast) intp 0.419064424001 (87.08% as fast) # python3.4 concatenate_test.py plus 0.481188605998 (85.14% as fast) join 0.409673971997 (100.00% as fast) form 0.652010936996 (62.83% as fast) intp 0.460400978001 (88.98% as fast) # python3.5 concatenate_test.py plus 0.417167026084 (93.47% as fast) join 0.389929617057 (100.00% as fast) form 0.595661019906 (65.46% as fast) intp 0.404455224983 (96.41% as fast)

Lección aprendida:

  • A veces, mis suposiciones están completamente equivocadas.
  • Prueba contra el sistema env estarás ejecutando en producción.
  • La interpolación de cadenas aún no está muerta.

tl; dr:

  • Si usa 2.6, use el operador +.
  • si está usando 2.7 use el operador ''%''.
  • si está usando 3.x use '''' .join ().

Un año después, probemos la respuesta de mkoistinen con python 3.4.3:

  • más 0.963564149000 (95.83% más rápido)
  • join 0.923408469000 (100.00% tan rápido)
  • formulario 1.501130934000 (61.51% tan rápido)
  • intp 1.019677452000 (90.56% tan rápido)

Nada ha cambiado. Unirse sigue siendo el método más rápido. Sin embargo, con el intp siendo la mejor opción en términos de legibilidad, es posible que desee utilizar intp.


depende de los tamaños relativos de la nueva cadena después de cada nueva concatenación. Con el operador + , por cada concatenación se crea una nueva cadena. Si las cadenas intermedias son relativamente largas, el + vuelve cada vez más lento porque se almacena la nueva cadena intermediaria.

Considera este caso:

from time import time stri='''' a=''aagsdfghfhdyjddtyjdhmfghmfgsdgsdfgsdfsdfsdfsdfsdfsdfddsksarigqeirnvgsdfsdgfsdfgfg'' l=[] #case 1 t=time() for i in range(1000): stri=stri+a+repr(i) print time()-t #case 2 t=time() for i in xrange(1000): l.append(a+repr(i)) z=''''.join(l) print time()-t #case 3 t=time() for i in range(1000): stri=stri+repr(i) print time()-t #case 4 t=time() for i in xrange(1000): l.append(repr(i)) z=''''.join(l) print time()-t

Resultados

1 0.00493192672729

2 0.000509023666382

3 0.00042200088501

4 0.000482797622681

En el caso de 1 y 2, agregamos una cadena grande, y join () funciona unas 10 veces más rápido. En el caso 3 y 4, agregamos una pequeña cadena, y ''+'' se realiza un poco más rápido


esta url tiene las comparaciones de los diferentes enfoques junto con algunas evaluaciones comparativas:

here

Tenga en cuenta: Esta es una comparación muy antigua de pre-2009 basada en Python 2.2, por lo que, en la mayoría de los casos, debe descartarse.

''''.join(sequenceofstrings) es lo que generalmente funciona mejor, lo más simple y rápido.