vectores unir listas lista concatenar concatenacion como arreglos python string python-3.x concat

unir - ¿Cuál es la forma preferida de concatenar una cadena en Python?



string en python (11)

Dado que la string de Python no se puede cambiar, me preguntaba cómo concatenar una cadena de manera más eficiente.

Puedo escribir así:

s += stringfromelsewhere

o así:

s = [] s.append(somestring) later s = ''''.join(s)

Mientras escribía esta pregunta, encontré un buen artículo que hablaba sobre el tema.

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

Pero está en Python 2.x., ¿entonces la pregunta sería si algo cambió en Python 3?


Aunque algo anticuado, Code Like a Pythonista: Idiomatic Python recomienda join() sobre + en esta sección . Al igual que PythonSpeedPerformanceTips en su sección sobre concatenación de cadenas , con el siguiente descargo de responsabilidad:

La precisión de esta sección se disputa con respecto a las versiones posteriores de Python. En CPython 2.5, la concatenación de cadenas es bastante rápida, aunque puede que no se aplique igualmente a otras implementaciones de Python. Ver ConcatenationTestCode para una discusión.


Como @jdi menciona, la documentación de Python sugiere usar str.join o io.StringIO para la concatenación de cadenas. Y dice que un desarrollador debe esperar un tiempo cuadrático de += en un bucle, aunque haya una optimización desde Python 2.4. Como dice this respuesta:

Si Python detecta que el argumento de la izquierda no tiene otras referencias, llama a realloc para intentar evitar una copia cambiando el tamaño de la cadena en su lugar. Esto no es algo en lo que deba confiar, porque es un detalle de implementación y porque si realloc necesita mover la cadena con frecuencia, el rendimiento se degrada a O (n ^ 2) de todos modos.

Mostraré un ejemplo de código del mundo real que ingenuamente se basó en += esta optimización, pero no se aplicó. El siguiente código convierte un iterable de cadenas cortas en trozos más grandes para usar en una API masiva.

def test_concat_chunk(seq, split_by): result = [''''] for item in seq: if len(result[-1]) + len(item) > split_by: result.append('''') result[-1] += item return result

Este código puede ejecutarse literariamente durante horas debido a la complejidad cuadrática del tiempo. A continuación se presentan alternativas con estructuras de datos sugeridas:

import io def test_stringio_chunk(seq, split_by): def chunk(): buf = io.StringIO() size = 0 for item in seq: if size + len(item) <= split_by: size += buf.write(item) else: yield buf.getvalue() buf = io.StringIO() size = buf.write(item) if size: yield buf.getvalue() return list(chunk()) def test_join_chunk(seq, split_by): def chunk(): buf = [] size = 0 for item in seq: if size + len(item) <= split_by: buf.append(item) size += len(item) else: yield ''''.join(buf) buf.clear() buf.append(item) size = len(item) if size: yield ''''.join(buf) return list(chunk())

Y un micro-benchmark:

import timeit import random import string import matplotlib.pyplot as plt line = ''''.join(random.choices( string.ascii_uppercase + string.digits, k=512)) + ''/n'' x = [] y_concat = [] y_stringio = [] y_join = [] n = 5 for i in range(1, 11): x.append(i) seq = [line] * (20 * 2 ** 20 // len(line)) chunk_size = i * 2 ** 20 y_concat.append( timeit.timeit(lambda: test_concat_chunk(seq, chunk_size), number=n) / n) y_stringio.append( timeit.timeit(lambda: test_stringio_chunk(seq, chunk_size), number=n) / n) y_join.append( timeit.timeit(lambda: test_join_chunk(seq, chunk_size), number=n) / n) plt.plot(x, y_concat) plt.plot(x, y_stringio) plt.plot(x, y_join) plt.legend([''concat'', ''stringio'', ''join''], loc=''upper left'') plt.show()


El método recomendado aún es usar agregar y unir.


El uso de la concatenación de cadenas en lugar de ''+'' es EL MEJOR método de concatenación en términos de estabilidad e implementación cruzada, ya que no admite todos los valores. El estándar PEP8 desalienta esto y fomenta el uso de format (), join () y append () para uso a largo plazo.


En Python> = 3.6, la nueva f-string es una forma eficiente de concatenar una cadena.

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


La mejor manera de agregar una cadena a una variable de cadena es usar + o += . Esto es porque es legible y rápido. También son igual de rápidos, el que elijas es una cuestión de gustos, este último es el más común. Aquí están los tiempos con el módulo timeit :

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

Sin embargo, aquellos que recomiendan tener listas y agregarlas y luego unirse a esas listas, lo hacen porque la adición de una cadena a una lista es probablemente muy rápida en comparación con la extensión de una cadena. Y esto puede ser cierto, en algunos casos. Aquí, por ejemplo, hay un millón de apéndices de una cadena de un carácter, primero a una cadena, luego a una lista:

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

Bien, resulta que incluso cuando la cadena resultante tiene un millón de caracteres, la anexión fue aún más rápida.

Ahora intentemos agregar una cadena de mil caracteres cien mil veces:

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

La cadena final, por lo tanto, termina teniendo unos 100 MB de longitud. Eso fue bastante lento, agregarse a una lista fue mucho más rápido. Que ese tiempo no incluye la final a.join() . Entonces, ¿cuánto tiempo tomaría?

a.join(a): 0.43739795684814453

Oups. Resulta que incluso en este caso, añadir / unir es más lento.

Entonces, ¿de dónde viene esta recomendación? Python 2?

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

Bueno, agregar / unir es ligeramente más rápido allí si estás usando cadenas extremadamente largas (lo que normalmente no lo haces, ¿qué tendrías en una memoria de 100 MB?)

Pero el verdadero factor decisivo es Python 2.3. Donde ni siquiera te mostraré los horarios, porque es tan lento que aún no ha terminado. Estas pruebas de repente toman minutos . Excepto el append / join, que es tan rápido como en Pythons posteriores.

Sip. La concatenación de cuerdas fue muy lenta en Python en la edad de piedra. Pero en la versión 2.4 ya no está (o al menos Python 2.4.7), por lo que la recomendación de usar append / join quedó obsoleta en 2008, cuando Python 2.3 dejó de actualizarse, y debería haber dejado de usarlo. :-)

(Actualización: Resulta que cuando hice las pruebas con más cuidado, usar + y += es más rápido para dos cadenas en Python 2.3. La recomendación de usar ''''.join() debe ser un malentendido)

Sin embargo, esto es CPython. Otras implementaciones pueden tener otras preocupaciones. Y esta es una razón más por la cual la optimización prematura es la raíz de todo mal. No utilice una técnica que se supone "más rápida" a menos que primero la mida.

Por lo tanto, la "mejor" versión para hacer concatenación de cadenas es usar + o + = . Y si eso resulta ser lento para ti, lo cual es bastante improbable, entonces haz algo más.

Entonces, ¿por qué uso un montón de añadir / unirme a mi código? Porque a veces en realidad es más claro. Especialmente cuando todo lo que debe concatenar debe estar separado por espacios o comas o nuevas líneas.



Si estás concatenando muchos valores, entonces ninguno. Anexar una lista es caro. Puedes usar StringIO para eso. Especialmente si lo está construyendo sobre muchas operaciones.

from cStringIO import StringIO # python3: from io import StringIO buf = StringIO() buf.write(''foo'') buf.write(''foo'') buf.write(''foo'') buf.getvalue() # ''foofoofoo''

Si ya tiene una lista completa devuelta a usted de alguna otra operación, simplemente use ''''.join(aList)

De las Preguntas frecuentes de python: ¿Cuál es la forma más eficiente de concatenar muchas cadenas juntas?

Los objetos str y bytes son inmutables, por lo que la concatenación de muchas cadenas es ineficiente ya que cada concatenación crea un nuevo objeto. En el caso general, el costo total de tiempo de ejecución es cuadrático en la longitud total de la cadena.

Para acumular muchos objetos str, el idioma recomendado es colocarlos en una lista y llamar a str.join () al final:

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

(otro idioma razonablemente eficiente es usar io.StringIO)

Para acumular muchos objetos de bytes, el idioma recomendado es extender un objeto bytearray utilizando la concatenación en el lugar (el operador + =):

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

Edición: era tonto y tenía los resultados pegados al revés, por lo que parece que agregarse a una lista fue más rápido que cStringIO. También he agregado pruebas para bytearray / str concat, así como una segunda ronda de pruebas que utilizan una lista más grande con cadenas más grandes. (Python 2.7.3)

Ejemplo de prueba ipython para grandes listas de cadenas

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


Si las cadenas que está concatenando son literales, use la concatenación literal de cadenas.

re.compile( "[A-Za-z_]" # letter or underscore "[A-Za-z0-9_]*" # letter, digit or underscore )

Esto es útil si desea comentar parte de una cadena (como se muestra arriba) o si desea usar cadenas sin formato o comillas triples para una parte de un literal pero no todas.

Como esto ocurre en la capa de sintaxis, utiliza operadores de concatenación cero.


Tu escribes esta funcion

>>> name = ''some_name'' >>> number = 123 >>> >>> f''Name is {name} and the number is {number}.'' ''Name is some_name and the number is 123.''

Entonces puedes llamar simplemente a donde quieras

s += "%s" %(stringfromelsewhere)


mi caso de uso era ligeramente diferente Tuve que construir una consulta donde más de 20 campos eran dinámicos. Seguí este enfoque de usar el método de formato

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

esto fue comparativamente más simple para mí en lugar de usar + u otras formas