¿Clase de cadena de Python como StringBuilder en C #?

121

¿Hay alguna clase de cadena en Python como StringBuilderen C #?

icn
fuente
6
Este es un duplicado del equivalente en Python de Java StringBuffer . PRECAUCIÓN: Las respuestas aquí están muy desactualizadas y, de hecho, se han vuelto engañosas. Vea esa otra pregunta para obtener respuestas que sean más relevantes para las versiones modernas de Python (ciertamente 2.7 y superiores).
Jean-François Corbett

Respuestas:

102

No existe una correlación uno a uno. Para obtener un artículo realmente bueno, consulte Concatenación eficiente de cadenas en Python :

La construcción de cadenas largas en el lenguaje de programación Python a veces puede resultar en un código de ejecución muy lenta. En este artículo investigo el rendimiento computacional de varios métodos de concatenación de cadenas.

Andrew Hare
fuente
27
Tenga en cuenta que este artículo se escribió en base a Python 2.2. Es probable que las pruebas resulten algo diferentes en una versión moderna de Python (CPython generalmente optimiza con éxito la concatenación, pero no desea depender de esto en el código importante) y una expresión generadora en la que usa una lista de comprensión sería digna de consideración. .
Mike Graham
4
Sería bueno incluir algunos aspectos destacados en ese artículo, al menos un par de implementaciones (para evitar problemas de rotura de enlaces).
jpmc26
3
Método 1: resultString + = appendString es el más rápido según las pruebas de @ Antoine-tran a continuación
Justas
5
Tu cotización no responde en absoluto a la pregunta. Incluya las partes relevantes en su propia respuesta, para cumplir con las nuevas pautas.
Financia la demanda de Monica el
27

He utilizado el código de Oliver Crow (enlace proporcionado por Andrew Hare) y lo he adaptado un poco para adaptar Python 2.7.3. (utilizando el paquete timeit). Ejecuté en mi computadora personal, Lenovo T61, 6GB RAM, Debian GNU / Linux 6.0.6 (squeeze).

Aquí está el resultado de 10,000 iteraciones:

método1: 0.0538418292999 segundos
tamaño de proceso 4800 kb
método2: 0.22602891922 segundos
tamaño de proceso 4960 kb
método3: 0.0605459213257 segundos
tamaño de proceso 4980 kb
método 4: 0.0544030666351 segundos
tamaño de proceso 5536 kb
método5: 0.0551080703735 segundos
tamaño de proceso 5272 kb
method6: 0.0542731285095 segundos
tamaño de proceso 5512 kb

y para 5,000,000 iteraciones (el método 2 fue ignorado porque corría demasiado lento, como para siempre):

método 1: 5.88603997231 segundos
tamaño de proceso 37976 kb
Method3: 8.40748500824 segundos
tamaño de proceso 38024 kb
método 4: 7,96380496025 segundos
tamaño de proceso 321968 kb
método5: 8.03666186333 segundos
tamaño de proceso 71720 kb
método6: 6.68192911148 segundos
tamaño de proceso 38240 kb

Es bastante obvio que los chicos de Python han hecho un gran trabajo para optimizar la concatenación de cadenas, y como dijo Hoare: "la optimización prematura es la raíz de todos los males" :-)

Antoine-tran
fuente
2
Al parecer, Hoare no acepta eso: hans.gerwitz.com/2004/08/12/…
Pimin Konstantin Kefaloukos
5
No es una optimización prematura para evitar optimizaciones frágiles que dependen del intérprete. Si alguna vez desea migrar a PyPy o se arriesga a encontrar uno de los muchos casos de fallas sutiles para la optimización, haga las cosas de la manera correcta.
Veedrac
1
Parece que el método 1 es más fácil de optimizar para el compilador.
mbomb007
25

Depender de las optimizaciones del compilador es frágil. Los puntos de referencia vinculados en la respuesta aceptada y los números proporcionados por Antoine-tran no son de confianza. Andrew Hare comete el error de incluir una llamada a repren sus métodos. Eso ralentiza todos los métodos por igual, pero oculta la verdadera penalización al construir la cadena.

Utilice join. Es muy rápido y más robusto.

$ ipython3
Python 3.5.1 (default, Mar  2 2016, 03:38:02) 
IPython 4.1.2 -- An enhanced Interactive Python.

In [1]: values = [str(num) for num in range(int(1e3))]

In [2]: %%timeit
   ...: ''.join(values)
   ...: 
100000 loops, best of 3: 7.37 µs per loop

In [3]: %%timeit
   ...: result = ''
   ...: for value in values:
   ...:     result += value
   ...: 
10000 loops, best of 3: 82.8 µs per loop

In [4]: import io

In [5]: %%timeit
   ...: writer = io.StringIO()
   ...: for value in values:
   ...:     writer.write(value)
   ...: writer.getvalue()
   ...: 
10000 loops, best of 3: 81.8 µs per loop
GrantJ
fuente
Sí, la reprllamada domina el tiempo de ejecución, pero no es necesario que el error sea personal.
Alex Reinking
3
@AlexReinking lo siento, nada personal significaba. No estoy seguro de qué te hizo pensar que era personal. Pero si fue el uso de sus nombres, los usé solo para referirme a las respuestas del usuario (coincide con los nombres de usuario, no estoy seguro de si hay una mejor manera).
GrantJ
1
buen ejemplo de sincronización que separa las operaciones de inicialización y concatenación de datos
aiodintsov
19

Python tiene varias cosas que cumplen propósitos similares:

  • Una forma común de construir cadenas grandes a partir de piezas es hacer crecer una lista de cadenas y unirlas cuando haya terminado. Este es un modismo de Python de uso frecuente.
    • Para crear cadenas que incorporen datos con formato, lo haría por separado.
  • Para la inserción y eliminación a nivel de carácter, debe mantener una lista de cadenas de una longitud. (Para hacer esto a partir de una cadena, llamarías list(your_string). También podrías usar a UserString.MutableStringpara esto.
  • (c)StringIO.StringIO es útil para cosas que de otro modo tomarían un archivo, pero menos para la construcción de cadenas en general.
Mike Graham
fuente
10

Usando el método 5 de arriba (El Pseudo Archivo) podemos obtener muy buen rendimiento y flexibilidad

from cStringIO import StringIO

class StringBuilder:
     _file_str = None

     def __init__(self):
         self._file_str = StringIO()

     def Append(self, str):
         self._file_str.write(str)

     def __str__(self):
         return self._file_str.getvalue()

ahora usándolo

sb = StringBuilder()

sb.Append("Hello\n")
sb.Append("World")

print sb
Thomas Watson
fuente
-1

No hay un análogo explícito: creo que se espera que use concatenaciones de cadenas (probablemente optimizadas como se dijo antes) o clases de terceros (dudo que sean mucho más eficientes: las listas en Python son de tipo dinámico, por lo que no funcionan rápidamente char [] para búfer como supongo). Las clases de tipo Stringbuilder no son una optimización prematura debido a la característica innata de las cadenas en muchos idiomas (inmutabilidad), que permite muchas optimizaciones (por ejemplo, hacer referencia al mismo búfer para cortes / subcadenas). Las clases Stringbuilder / stringbuffer / stringstream-like funcionan mucho más rápido que la concatenación de cadenas (produciendo muchos objetos temporales pequeños que aún necesitan asignaciones y recolección de basura) e incluso herramientas de formato de cadena de tipo printf, sin necesidad de interpretar la sobrecarga del patrón de formato que consume bastante para muchas llamadas de formato.

Cerebro
fuente
-4

En caso de que esté buscando un método rápido de concatenación de cadenas en Python, entonces no necesita una clase especial StringBuilder. La concatenación simple funciona igual de bien sin la penalización de rendimiento que se ve en C #.

resultString = ""

resultString += "Append 1"
resultString += "Append 2"

Vea la respuesta de Antoine-tran para obtener resultados de rendimiento

Tal como
fuente