Question

En Python, où et quand l'utilisation de la concaténation de chaîne par rapport à la substitution de chaîne me échappe. Comme la concaténation de chaîne a vu de grandes performances dans boosts, 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 des 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 l'adhésion à une liste de chaînes et pour utiliser la substitution du nom. Ce sont des variantes sur le thème central, qui est, où est la bonne façon de le faire à ce moment? Merci pour les réponses!

Était-ce utile?

La solution

est Concaténation (beaucoup) plus rapide selon ma machine. Mais stylistiquement, je suis prêt à payer le prix de substitution si les performances n'est pas critique. Eh bien, et si je besoin de mise en forme, il n'y a même pas besoin de poser la question ... il n'y a pas d'autre choix que d'utiliser l'interpolation / templating.

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

Autres conseils

Ne pas oublier la substitution du nom:

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

Méfiez-vous des chaînes concaténer dans une boucle! Le coût de concaténation de chaîne est proportionnelle à la longueur du résultat. Looping vous mène directement à la terre de N-carré. Certaines langues optimiseront concaténation à la chaîne la plus récemment alloué, mais il est risqué de compter sur le compilateur pour optimiser votre algorithme quadratique jusqu'à linéaire. Il vaut mieux utiliser la primitive (join?) Qui prend toute une liste de chaînes, ne une seule allocation, et les concaténer en une seule fois.

« Comme la concaténation de chaîne a vu de grandes performances dans ... booste »

Si les questions de performance, cela est bon à savoir.

Cependant, les problèmes de performance que j'ai vu ne se sont jamais vers le bas pour les opérations de chaîne. J'ai généralement eu des démêlés avec E / S, le tri et O ( n 2 ) opérations étant les goulots d'étranglement.

Jusqu'à ce que les opérations de chaîne sont les limiteurs de performance, je vais rester avec les choses qui sont évidentes. La plupart du temps, c'est la substitution quand il est une ligne ou moins, concaténation quand il est logique, et un outil de modèle (comme Mako) quand il est grand.

Qu'est-ce que vous voulez concaténer / interpoler et comment vous voulez formater le résultat devrait conduire votre décision.

  • interpolation de chaîne vous permet d'ajouter facilement la mise en forme. 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 devez écrire return DOMAIN + QUESTIONS + "/" + str(q_num) dans cet exemple.

  • facilite l'interpolation à formater des valeurs numériques; "%d of %d (%2.2f%%)" % (current, total, total/current) serait beaucoup moins lisible sous forme de concaténation.

  • Concaténation est utile lorsque vous ne disposez pas d'un nombre fixe d'éléments à chaîne-iser.

De plus, sachez que Python 2.6 introduit une nouvelle version d'interpolation de chaîne, appelée string templating:

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

Chaîne templating est prévu pour remplacer à terme% -interpolation, mais cela ne se produira pas pendant un certain temps, je pense.

Je testais juste la vitesse des différentes méthodes concaténation / substitution chaîne par curiosité. Une recherche Google sur le sujet m'a amené ici. Je pensais que je posterais mes résultats des tests 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 exécuté runtests((percent_, format_, format2_, concat_), runs=5), je trouve que la méthode% était environ deux fois plus vite que les autres sur ces petites chaînes. La méthode concat a toujours été le plus lent (à peine). Il y avait des différences très minuscules lors du changement des positions dans la méthode format(), mais les positions de commutation a toujours été au moins .01 plus lent que la méthode de format régulier.

Exemple des résultats des tests:

    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 derniers parce que j'utilise concaténation de chaîne dans mes scripts, et je me demandais ce que le coût était. Je les ai couru dans des ordres différents pour assurer que rien interférait, ou d'obtenir de meilleures performances étant le premier ou le dernier. Sur une note de côté, j'ai jeté dans certains générateurs de chaînes plus longues dans ces fonctions comme "%s" + ("a" * 1024) et concat régulière était presque 3 fois plus vite que l'utilisation des méthodes de format et % (1,1 vs 2,8). Je suppose que cela dépend des chaînes, et ce que vous essayez d'atteindre. Si la performance est vraiment, il pourrait être préférable d'essayer différentes choses et de les tester. J'ai tendance à choisir la lisibilité sur la vitesse, à moins que la vitesse devient un problème, mais c'est juste moi. SO n'a pas aimé ma copier / coller, je devais mettre 8 places sur tout pour le faire paraître droite. Je l'habitude d'utiliser 4.

Rappelez-vous, les décisions stylistiques sont décisions pratiques, si vous prévoyez toujours sur le maintien ou le débogage du code :-) Il y a une citation célèbre de Knuth (peut-être citer Hoare?): « Nous devrions oublier petit l'efficacité, disons environ 97% du temps: l'optimisation prématurée est la racine de tous les maux »

.

Tant que vous faites attention à ne pas (par exemple) tourner un O (n) tâche dans un O (n 2 ) tâche, j'aller avec celui que vous trouverez plus facile à comprendre ..

J'utilise la substitution chaque fois que je peux. Je n'utilise que si je concaténation la construction d'une chaîne en dire une boucle for.

En fait, la bonne chose à faire, dans ce cas (chemins de construction) est d'utiliser os.path.join. Non concaténation de chaînes ou d'interpolation

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top