Concaténation des chaînes de caractères vs. substitution des chaînes de caractères en Python

en Python, le lieu et le moment de l'utilisation de la concaténation de la chaîne versus la substitution de la chaîne m'échappe. Comme la concaténation de la corde a vu de grands boosts dans la performance, est-ce (de plus en plus) une décision stylistique plutôt que pratique?

pour un exemple concret, comment doit-on gérer la construction d'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: il y a aussi eu des suggestions sur la façon de joindre une liste de chaînes et d'utiliser named substitution. Ce sont des variantes sur le thème central, qui est, Quelle est la bonne façon de le faire à quel moment? Merci pour les réponses!

98
demandé sur gotgenes 2008-12-18 02:39:39

9 réponses

la concaténation est (significativement) plus rapide selon ma machine. Mais stylistiquement, je suis prêt à payer le prix de la substitution si la performance n'est pas critique. Et bien, et si j'ai besoin de formatage, il n'y a même pas besoin de poser la question... il n'y a pas d'autre option que d'utiliser l'interpolation/Templier.

>>> 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
55
répondu Vinko Vrsalovic 2015-06-21 06:41:58

n'oubliez pas la substitution de nom:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()
23
répondu too much php 2008-12-18 00:22:59

méfiez-vous des cordes concaténantes dans une boucle! le coût de la concaténation de la corde est proportionnel à la longueur du résultat. La boucle vous mène directement à la terre de N-carré. Certains langages optimiseront la concaténation vers la chaîne la plus récemment attribuée, mais il est risqué de compter sur le compilateur pour optimiser votre algorithme quadratique jusqu'à linéaire. Mieux utiliser le primitif ( join ?) qui prend une liste entière de chaînes, fait un simple et les concaténate tous en une seule fois.

12
répondu Norman Ramsey 2008-12-18 04:40:07

" comme la chaîne concaténation a vu de grands boosts dans la performance..."

Si la performance est importante, c'est bon à savoir.

cependant, les problèmes de performance que j'ai vu ne sont jamais descendus aux opérations de chaîne. J'ai généralement eu des problèmes avec l'E / S, le tri et O ( n 2 ) les opérations étant les goulets d'étranglement.

Jusqu'à ce que les opérations de chaîne de caractères soient les limiteurs de performance, Je vais m'en tenir à des choses qui sont évidentes. La plupart du temps, c'est la substitution quand c'est une ligne ou moins, la concaténation quand ça a du sens, et un outil de modèle (comme Mako) quand c'est grand.

11
répondu S.Lott 2008-12-18 00:18:13

Ce que vous voulez concaténer/interpoler et comment vous voulez formater le résultat devrait conduire votre décision.

  • l'interpolation de chaîne vous permet d'ajouter facilement le formatage. En fait, votre version d'interpolation de chaîne ne fait pas la même chose que votre version de concaténation; elle ajoute en fait une barre oblique avant le paramètre q_num . Pour faire la même chose, vous devez écrire return DOMAIN + QUESTIONS + "/" + str(q_num) dans cet exemple.

  • L'Interpolation facilite le formatage des chiffres; "%d of %d (%2.2f%%)" % (current, total, total/current) serait beaucoup moins lisible sous forme de concaténation.

  • la concaténation est utile lorsque vous n'avez pas un nombre fixe d'éléments à string-ize.

sachez aussi que Python 2.6 introduit une nouvelle version d'interpolation de chaîne, appelée Templier de chaîne :

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

Templier de chaîne de caractères est prévu pour éventuellement remplacer % - interpolation, mais cela n'arrivera pas avant un certain temps, je pense.

10
répondu Tim Lesher 2008-12-18 00:18:50

j'étais juste en train de tester la vitesse de différentes méthodes de concaténation/substitution de cordes par curiosité. Une recherche google sur le sujet m'a amené ici. Je pensais afficher mes résultats de test dans l'espoir que cela pourrait aider quelqu'un à décider.

    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)

...Après avoir lancé runtests((percent_, format_, format2_, concat_), runs=5) , j'ai trouvé que la méthode % était environ deux fois plus rapide que les autres sur ces petites cordes. La méthode concat a toujours été la plus lente (à peine). Il y avait de très petites différences lors de la commutation des positions dans la méthode format() , mais la commutation des positions a toujours été au moins .01 plus lente que la méthode du format régulier.

échantillon des résultats d'épreuve:

    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)

j'ai couru ces parce que j'utilise la concaténation de chaîne de caractères dans mes scripts, et je me demandais quel était le coût. Je les ai passées dans différents ordres pour m'assurer que rien n'interférait, ou que la meilleure performance soit la première ou la dernière. Sur une note de côté, je l'ai jeté dans certains les générateurs de chaîne plus longs dans ces fonctions comme "%s" + ("a" * 1024) et la concat régulière était presque 3 fois plus rapide (1.1 vs 2.8) que l'utilisation des méthodes format et % . Je suppose que ça dépend des cordes, et de ce que vous essayez d'atteindre. Si la performance compte vraiment, il serait peut-être préférable d'essayer différentes choses et de les tester. J'ai tendance à choisir la lisibilité plutôt que la vitesse, sauf si la vitesse devient un problème, mais c'est juste moi. Donc je n'ai pas aimé mon copier / coller, j'ai dû mettre 8 espaces sur tout pour faire regarder à droite. J'en utilise habituellement 4.

7
répondu Cj Welborn 2013-05-13 03:27:35

rappelez-vous, les décisions stylistiques sont les décisions pratiques, si vous jamais projetez sur le maintien ou le débogage de votre code : -) Il ya une citation célèbre de Knuth (peut-être citant Hoare?): "Nous devrions oublier les petites efficacités, disons environ 97% du temps: l'optimisation prématurée est la racine de tout le mal."

aussi longtemps que vous faites attention à ne pas (dire) transformer une tâche O( n ) en une tâche O( n 2 ), j'irais avec qui vous voulez trouver plus facile à comprendre..

4
répondu John Fouhy 2008-12-18 01:04:35

j'utilise la substitution partout où je peux. Je n'utilise la concaténation que si je construis une chaîne dans une boucle.

0
répondu Draemon 2008-12-17 23:56:12

en fait, la bonne chose à faire, dans ce cas (construire des chemins) est d'utiliser os.path.join . Pas de concaténation ou d'interpolation de chaîne

-1
répondu hoskeri 2011-08-20 03:56:37