sirve - tamaño string python
¿Alguna razón para no usar ''+'' para concatenar dos cadenas? (7)
Un antipattern común en Python es concatenar una secuencia de cadenas usando +
en un bucle. Esto es malo porque el intérprete de Python tiene que crear un nuevo objeto de cadena para cada iteración, y termina tomando un tiempo cuadrático. (Las versiones recientes de CPython aparentemente pueden optimizar esto en algunos casos, pero otras implementaciones no, por lo que los programadores se desaniman de confiar en esto.) ''''.join
es la forma correcta de hacerlo.
Sin embargo, he oído decir ( incluso aquí en Stack Overflow ) que nunca debes usar +
para la concatenación de cadenas, sino que siempre usas ''''.join
o una cadena de formato. No entiendo por qué este es el caso si solo concatena dos cadenas. Si mi comprensión es correcta, no debería tomar un tiempo cuadrático, y creo que a + b
es más limpio y más legible que cualquiera ''''.join((a, b))
o ''%s%s'' % (a, b)
¿Es una buena práctica usar +
para concatenar dos cadenas? ¿O hay un problema del que no estoy enterado?
Cuando se trabaja con varias personas, a veces es difícil saber exactamente qué está sucediendo. Usar una cadena de formato en lugar de concatenación puede evitar una molestia particular que nos ha sucedido un montón de veces:
Digamos que una función requiere un argumento y la escribes esperando obtener una cadena:
In [1]: def foo(zeta):
...: print ''bar: '' + zeta
In [2]: foo(''bang'')
bar: bang
Por lo tanto, esta función se puede usar con bastante frecuencia en todo el código. Sus compañeros de trabajo pueden saber exactamente lo que hace, pero no necesariamente estar completamente al día en lo interno, y pueden no saber que la función espera una cadena. Y entonces pueden terminar con esto:
In [3]: foo(23)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/izkata/<ipython console> in <module>()
/home/izkata/<ipython console> in foo(zeta)
TypeError: cannot concatenate ''str'' and ''int'' objects
No habría problema si acaba de utilizar una cadena de formato:
In [1]: def foo(zeta):
...: print ''bar: %s'' % zeta
...:
...:
In [2]: foo(''bang'')
bar: bang
In [3]: foo(23)
bar: 23
Lo mismo es cierto para todos los tipos de objetos que definen __str__
, que también pueden pasarse:
In [1]: from datetime import date
In [2]: zeta = date(2012, 4, 15)
In [3]: print ''bar: '' + zeta
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/izkata/<ipython console> in <module>()
TypeError: cannot concatenate ''str'' and ''datetime.date'' objects
In [4]: print ''bar: %s'' % zeta
bar: 2012-04-15
Entonces sí: si puede usar una cadena de formato, hágalo y aproveche lo que Python tiene para ofrecer.
De acuerdo con los documentos de Python, el uso de str.join () le dará consistencia de rendimiento en varias implementaciones de Python. Aunque CPython optimiza el comportamiento cuadrático de s = s + t, otras implementaciones de Python pueden no hacerlo.
Detalle de implementación de CPython : si s y t son cadenas, algunas implementaciones de Python como CPython generalmente pueden realizar una optimización in situ para asignaciones de la forma s = s + t o s + = t. Cuando corresponda, esta optimización hace que el tiempo de ejecución cuadrático sea mucho menos probable. Esta optimización depende tanto de la versión como de la implementación. Para el código sensible al rendimiento, es preferible utilizar el método str.join () que asegura un rendimiento de concatenación lineal consistente en las versiones y las implementaciones.
Tipos de secuencia en documentos Python (ver la nota al pie [6])
El operador Plus es la solución perfecta para concatenar dos cadenas de Python. Pero si continúa agregando más de dos cadenas (n> 25), es posible que desee pensar en otra cosa.
''''.join([a, b, c])
es una optimización del rendimiento.
He hecho una prueba rápida:
import sys
str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx/n"
for i in range(int(sys.argv[1])):
str = str + e
y lo cronometraron:
mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 8000000
8000000 times
real 0m2.165s
user 0m1.620s
sys 0m0.540s
mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py 16000000
16000000 times
real 0m4.360s
user 0m3.480s
sys 0m0.870s
Aparentemente hay una optimización para el caso a = a + b
. No muestra el tiempo O (n ^ 2) como uno podría sospechar.
Entonces, al menos en términos de rendimiento, usar +
está bien.
La suposición de que uno nunca debe usar + para la concatenación de cadenas, sino que siempre usa '''' .unirse puede ser un mito. Es cierto que el uso de +
crea copias temporales innecesarias de objetos de cadena inmutables, pero el otro hecho que no es citado es que la join
llamadas en un ciclo generalmente agrega la sobrecarga de la function call
. Tomemos tu ejemplo.
Cree dos listas, una de la pregunta SO vinculada y otra más grande fabricada
>>> myl1 = [''A'',''B'',''C'',''D'',''E'',''F'']
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)]
Permite crear dos funciones, UseJoin
y UsePlus
para usar la join
respectiva y la funcionalidad +
.
>>> def UsePlus():
return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)]
>>> def UseJoin():
[''''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)]
Vamos a ejecutar timeit con la primera lista
>>> myl=myl1
>>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus")
>>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
2.48 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
2.61 usec/pass
>>>
Tienen casi el mismo tiempo de ejecución.
Permite usar cProfile
>>> myl=myl2
>>> cProfile.run("UsePlus()")
5 function calls in 0.001 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.001 0.001 0.001 0.001 <pyshell#1376>:1(UsePlus)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {len}
1 0.000 0.000 0.000 0.000 {method ''disable'' of ''_lsprof.Profiler'' objects}
1 0.000 0.000 0.000 0.000 {range}
>>> cProfile.run("UseJoin()")
5005 function calls in 0.029 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.015 0.015 0.029 0.029 <pyshell#1388>:1(UseJoin)
1 0.000 0.000 0.029 0.029 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {len}
1 0.000 0.000 0.000 0.000 {method ''disable'' of ''_lsprof.Profiler'' objects}
5000 0.014 0.000 0.014 0.000 {method ''join'' of ''str'' objects}
1 0.000 0.000 0.000 0.000 {range}
Y parece que el uso de Join genera llamadas de función innecesarias que podrían agregarse a la sobrecarga.
Ahora volviendo a la pregunta. ¿Debería desalentarse el uso de +
sobre join
en todos los casos?
Yo creo que no, las cosas deberían tomarse en consideración
- Longitud de la cadena en cuestión
- No de operación de concatenación.
Y fuera de curso en una optimización pre-madura de desarrollo es malo.
No hay nada de malo en concatenar dos cadenas con +
. De hecho, es más fácil de leer que ''''.join([a, b])
.
Sin embargo, tienes razón en que concatenar más de 2 cadenas con +
es una operación O (n ^ 2) (en comparación con O (n) para join
) y por lo tanto se vuelve ineficiente. Sin embargo, esto no tiene que ver con el uso de un bucle. Incluso a + b + c + ...
es O (n ^ 2), la razón es que cada concatenación produce una nueva cadena.
CPython2.4 y superior intentan atenuar eso, pero aún así es aconsejable usar join
cuando concatena más de 2 cadenas.
'''' .join ([a, b]) es una mejor solución que + .
Porque Code debe escribirse de una manera que no perjudique a otras implementaciones de Python (PyPy, Jython, IronPython, Cython, Psyco, etc.)
form a + = b o a = a + b es frágil incluso en CPython y no está presente en las implementaciones que no utilizan refcounting (el recuento de referencias es una técnica para almacenar la cantidad de referencias, punteros o identificadores en una recurso tal como un objeto, bloque de memoria, espacio en disco u otro recurso )
https://www.python.org/dev/peps/pep-0008/#programming-recommendations