web-dev-qa-db-fra.com

Concaténation de chaînes et substitution de chaînes dans Python

En Python, où et quand utiliser la concaténation de chaînes par rapport à la substitution de chaînes m'échappent. Comme la concaténation des chaînes a considérablement augmenté les performances, s’agit-il d’une décision stylistique plutôt que pratique?

Pour un exemple concret, comment gérer la construction d’URI 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 a également été suggéré de rejoindre une liste de chaînes et d’utiliser la substitution nommée. Ce sont des variantes du thème central: quelle est la bonne façon de le faire à quel moment? Merci pour les réponses!

98
gotgenes

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. Eh bien, et si j'ai besoin de formatage, il n'est même pas nécessaire de poser la question ... il n'y a pas d'autre choix que d'utiliser l'interpolation/la modélisation.

>>> 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
Vinko Vrsalovic

N'oubliez pas de substitution nommée:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()
23
too much php

Attention aux concaténations de chaînes dans une boucle! Le coût de la concaténation de chaînes est proportionnel à la longueur du résultat. La boucle vous mène directement au pays de N-squared. Certaines langues vont optimiser la concaténation avec la dernière chaîne allouée, mais il est risqué de compter sur le compilateur pour optimiser votre algorithme quadratique en linéaire. Il est préférable d’utiliser la primitive (join?) Qui prend une liste complète de chaînes, effectue une seule attribution et les concatène en une seule fois.

12
Norman Ramsey

"La concaténation des chaînes a considérablement augmenté les performances ..."

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

Cependant, les problèmes de performances que j'ai vus ne sont jamais liés à des opérations sur les chaînes. J'ai généralement eu des problèmes avec les E/S, le tri et O (n2) opérations constituant les goulots d'étranglement.

Tant que les opérations sur les chaînes ne limitent pas les performances, je vais m'en tenir aux choses évidentes. Généralement, il s'agit d'une substitution lorsqu'il s'agit d'une ligne ou moins, d'une concaténation lorsque cela a du sens, et d'un outil de modèle (comme Mako) lorsqu'il est volumineux.

11
S.Lott

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

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

  • L'interpolation facilite le formatage des données numériques; "%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 à structurer.

Sachez également que Python 2.6 introduit une nouvelle version de l’interpolation de chaîne, appelée modèle de chaîne) :

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

Les modèles de chaînes devraient remplacer éventuellement% -interpolation, mais cela n'arrivera pas avant un certain temps, je pense.

10
Tim Lesher

Je testais simplement la vitesse de différentes méthodes de concaténation/substitution de chaînes par curiosité. Une recherche google sur le sujet m'a amené ici. Je pensais publier mes résultats de test dans l'espoir que cela puisse aider quelqu'un à prendre une décision.

    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 exécuté runtests((percent_, format_, format2_, concat_), runs=5), j'ai constaté que la méthode% était environ deux fois plus rapide que les autres sur ces petites chaînes. La méthode concat était toujours la plus lente (à peine). Il y avait de très petites différences lors du changement de position dans la méthode format(), mais le changement de position était toujours au moins 0,01 plus lent que la méthode de formatage standard.

Exemple de résultats de test:

    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)

Je les ai exécutés car j’utilise la concaténation de chaînes dans mes scripts et je me demandais quel en était le coût. Je les ai exécutés dans différents ordres pour m'assurer que rien ne gênait ou que les performances étaient meilleures en premier ou en dernier. Sur une note de côté, j’ai ajouté des générateurs de chaînes plus longs à des fonctions telles que "%s" + ("a" * 1024) et le concat normal était presque 3 fois plus rapide (1.1 vs 2.8) que l’utilisation de format et % Méthodes. Je suppose que cela dépend des cordes et de ce que vous essayez d’atteindre. Si la performance compte vraiment, il 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, à moins que la vitesse ne devienne un problème, mais c'est juste moi. SO n'aimait pas mon copier/coller, je devais mettre 8 espaces sur tout pour le rendre correct. J'utilise habituellement 4.

7
Cj Welborn

Rappelez-vous, décisions stylistiques sont décisions pratiques, si vous envisagez de maintenir ou de déboguer votre code :-) Il existe une citation célèbre de Knuth (citant éventuellement Hoare?): "Nous devrions oublier les petites économies, disons environ 97% du temps: l'optimisation prématurée est la racine de tout mal. "

Tant que vous faites attention à ne pas (par exemple) transformer une tâche O(n)) en une tâche O (n2) tâche, je voudrais aller avec celui que vous trouvez le plus facile à comprendre ..

4
John Fouhy

J'utilise la substitution partout où je peux. Je n'utilise la concaténation que si je construis une chaîne, disons une boucle for.

0
Draemon