Pregunta

En Python, el lugar y el momento de utilizar la concatenación de cadenas frente sustitución de cadenas se me escapa. A medida que la concatenación de cadenas ha visto grandes aumentos en el rendimiento, es este (cada vez más) una decisión estilística en lugar de una práctica?

En un ejemplo concreto, cómo debería uno manejar construcción de URIs flexibles:

DOMAIN = 'http://stackoverflow.com'
QUESTIONS = '/questions'

def so_question_uri_sub(q_num):
    return "%s%s/%d" % (DOMAIN, QUESTIONS, q_num)

def so_question_uri_cat(q_num):
    return DOMAIN + QUESTIONS + '/' + str(q_num)

Edit: También ha habido sugerencias acerca de unirse a una lista de cadenas y para el uso de sustitución llamado. Estos son variantes sobre el tema central, que es, en qué dirección es la manera correcta de hacerlo en ese momento? Gracias por las respuestas!

¿Fue útil?

Solución

La concatenación es (significativamente) más rápido de acuerdo con mi máquina. Sin embargo, estilísticamente, estoy dispuesto a pagar el precio de sustitución si el rendimiento no es crítico. Bueno, y si necesito el formato, no hay necesidad incluso de hacer la pregunta ... no hay más remedio que utilizar la interpolación / plantillas.

>>> import timeit
>>> def so_q_sub(n):
...  return "%s%s/%d" % (DOMAIN, QUESTIONS, n)
...
>>> so_q_sub(1000)
'http://stackoverflow.com/questions/1000'
>>> def so_q_cat(n):
...  return DOMAIN + QUESTIONS + '/' + str(n)
...
>>> so_q_cat(1000)
'http://stackoverflow.com/questions/1000'
>>> t1 = timeit.Timer('so_q_sub(1000)','from __main__ import so_q_sub')
>>> t2 = timeit.Timer('so_q_cat(1000)','from __main__ import so_q_cat')
>>> t1.timeit(number=10000000)
12.166618871951641
>>> t2.timeit(number=10000000)
5.7813972166853773
>>> t1.timeit(number=1)
1.103492206766532e-05
>>> t2.timeit(number=1)
8.5206360154188587e-06

>>> def so_q_tmp(n):
...  return "{d}{q}/{n}".format(d=DOMAIN,q=QUESTIONS,n=n)
...
>>> so_q_tmp(1000)
'http://stackoverflow.com/questions/1000'
>>> t3= timeit.Timer('so_q_tmp(1000)','from __main__ import so_q_tmp')
>>> t3.timeit(number=10000000)
14.564135316080637

>>> def so_q_join(n):
...  return ''.join([DOMAIN,QUESTIONS,'/',str(n)])
...
>>> so_q_join(1000)
'http://stackoverflow.com/questions/1000'
>>> t4= timeit.Timer('so_q_join(1000)','from __main__ import so_q_join')
>>> t4.timeit(number=10000000)
9.4431309007150048

Otros consejos

No se olvide de sustitución llamado:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()

Sea cuidadoso de concatenación de cadenas en un bucle! El coste de la concatenación de cadenas es proporcional a la longitud del resultado. Bucle que conduce directamente a la tierra de N-cuadrado. Algunos idiomas optimizarán la concatenación de la cadena más recientemente asignado, pero es arriesgado contar con el compilador para optimizar su algoritmo de segundo grado hasta lineal. Lo mejor es utilizar la primitiva (join?) Que tiene una lista completa de las cadenas, hace una sola asignación, y todos ellos concatena en una sola vez.

"A medida que la concatenación de cadenas ha visto grandes aumentos en el rendimiento ..."

Si cuestiones de rendimiento, esto es bueno saber.

Sin embargo, los problemas de rendimiento que he visto nunca han llegado a las operaciones de cadenas. He conseguido generalmente en problemas con I / O, clasificación y O ( n 2 ) operaciones siendo los cuellos de botella.

Hasta operaciones de cadena son los limitadores de rendimiento, me quedo con las cosas que son obvias. Sobre todo, esa es la sustitución cuando es una línea o menos, la concatenación cuando tiene sentido, y una herramienta de plantilla (como Mako) cuando es grande.

¿Qué desea concatenar / interpolar y cómo desea formatear el resultado debe conducir su decisión.

  • interpolación de cadenas le permite añadir fácilmente el formato. De hecho, la versión de interpolación de cadenas no hace lo mismo que la versión de la concatenación; realmente añade una barra inclinada extra antes del parámetro q_num. Para hacer lo mismo, tendría que escribir return DOMAIN + QUESTIONS + "/" + str(q_num) en ese ejemplo.

  • La interpolación hace que sea más fácil para dar formato a los valores numéricos; "%d of %d (%2.2f%%)" % (current, total, total/current) sería mucho menos legible en forma de concatenación.

  • La concatenación es útil cuando usted no tiene un número fijo de elementos a cadena-ize.

También, sepa que Python 2.6 introduce una nueva versión de la interpolación de cadenas, llamado cadena de plantillas :

def so_question_uri_template(q_num):
    return "{domain}/{questions}/{num}".format(domain=DOMAIN,
                                               questions=QUESTIONS,
                                               num=q_num)

Cadena de plantillas está programado para reemplazar con el tiempo -interpolation%, pero eso no va a suceder durante bastante tiempo, creo.

Sólo estaba probando la velocidad de métodos concatenación / sustitución diferente de cuerda por curiosidad. Una búsqueda en Google sobre el tema me trajo aquí. Pensé que iba a publicar los resultados de mis pruebas con la esperanza de que podría ayudar a alguien a decidir.

    import timeit
    def percent_():
            return "test %s, with number %s" % (1,2)

    def format_():
            return "test {}, with number {}".format(1,2)

    def format2_():
            return "test {1}, with number {0}".format(2,1)

    def concat_():
            return "test " + str(1) + ", with number " + str(2)

    def dotimers(func_list):
            # runs a single test for all functions in the list
            for func in func_list:
                    tmr = timeit.Timer(func)
                    res = tmr.timeit()
                    print "test " + func.func_name + ": " + str(res)

    def runtests(func_list, runs=5):
            # runs multiple tests for all functions in the list
            for i in range(runs):
                    print "----------- TEST #" + str(i + 1)
                    dotimers(func_list)

... Después de ejecutar runtests((percent_, format_, format2_, concat_), runs=5), he encontrado que el método% fue aproximadamente dos veces más rápido que los otros en estas pequeñas cadenas. El método concat era siempre el más lento (apenas). Había muy pequeñas diferencias al cambiar las posiciones en el método format(), pero las posiciones de conmutación siempre fue al menos 0,01 lento que el método formato normal.

Ejemplo de resultados de la prueba:

    test concat_()  : 0.62  (0.61 to 0.63)
    test format_()  : 0.56  (consistently 0.56)
    test format2_() : 0.58  (0.57 to 0.59)
    test percent_() : 0.34  (0.33 to 0.35)

Me encontré con estos porque hago uso de la concatenación de cadenas en mis guiones, y me preguntaba cuál era el costo. Me encontré con ellos en diferentes órdenes para asegurarse de que nada se interfiera, o conseguir un mejor rendimiento siendo el primero o el último. En una nota lateral, me tiró en algunos generadores de cadenas largas en esas funciones como "%s" + ("a" * 1024) y concat regular fue casi 3 veces más rápido (1,1 frente a 2,8) como el uso de los métodos y format %. Supongo que depende de las cuerdas, y lo que está tratando de lograr. Si el rendimiento que realmente importa, que podría ser mejor para probar cosas diferentes y probarlos. Tiendo a elegir la legibilidad sobre la velocidad, a menos que la velocidad se convierte en un problema, pero eso es sólo yo. Así que no le gustó mi copiar / pegar, que tenía que poner 8 espacios en todo, para que se vea bien. Generalmente uso 4.

Recuerde, las decisiones estilísticas son decisiones prácticas, si usted planea siempre en el mantenimiento o la depuración del código :-) Hay una cita famosa de Knuth (posiblemente citando Hoare?): "Debemos olvidarnos de pequeña eficiencia, dicen que alrededor del 97% del tiempo: la optimización prematura es la raíz de todo mal "

.

Mientras usted tiene cuidado de no (por ejemplo) convertir una tarea O (n) en un O (n 2 ) la tarea, me iría con la que resulte más fácil de entender ..

Yo uso la sustitución en lo que pueda. Yo sólo uso concatenación si estoy construyendo una cadena en un ejemplo de circuito para.

En realidad lo correcto a hacer, en este caso (caminos de construcción) es utilizar os.path.join. No concatenación de cadenas o la interpolación

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top