98 votes

Concaténation de chaînes de caractères et substitution de chaînes de caractères en Python

En Python, je ne sais pas quand et comment utiliser la concaténation de chaînes de caractères par rapport à la substitution de chaînes de caractères. Comme la concaténation de chaînes de caractères a connu d'importants gains de performance, est-ce (de plus en plus) une décision stylistique plutôt qu'une décision pratique ?

Pour un exemple concret, comment 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 pour joindre une liste de chaînes de caractères et pour utiliser la substitution nommée. Ce sont des variantes du thème central, qui est de savoir quelle est la bonne façon de faire à quel moment ? Merci pour les réponses !

0 votes

C'est drôle, en Ruby, l'interpolation de chaînes est généralement plus rapide que la concaténation...

0 votes

Vous avez oublié de retourner "".join([DOMAINE, QUESTIONS, str(q_num)])

0 votes

Je ne suis pas un expert de Ruby, mais je parierais que l'interpolation est plus rapide parce que les chaînes de caractères sont mutables en Ruby. Les chaînes de caractères sont des séquences immuables en Python.

55voto

Vinko Vrsalovic Points 116138

La concaténation est (significativement) plus rapide selon ma machine. Mais stylistiquement, je suis prêt à payer le prix de la substitution si les performances ne sont pas critiques. Et si j'ai besoin de formatage, ce n'est même pas la peine de poser la question... il n'y a pas d'autre choix que d'utiliser l'interpolation/le templage.

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

10 votes

Avez-vous fait des tests avec de grandes chaînes de caractères (comme 100000 caractères) ?

24voto

too much php Points 27983

N'oubliez pas la substitution de noms :

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

4 votes

Ce code comporte au moins deux mauvaises pratiques de programmation : l'attente de variables globales (domain et questions ne sont pas déclarées à l'intérieur de la fonction) et le passage de plus de variables que nécessaire à une fonction format(). Downvoting parce que cette réponse enseigne de mauvaises pratiques de codage.

12voto

Norman Ramsey Points 115730

Méfiez-vous de la concaténation de chaînes de caractères dans une boucle ! Le coût de la concaténation de chaînes de caractères est proportionnel à la longueur du résultat. Les boucles vous mènent directement au pays de N-carré. Certains langages optimisent la concaténation sur 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 entière de chaînes de caractères, fait une seule allocation et les concatène toutes en une seule fois.

16 votes

Ce n'est pas d'actualité. Dans les dernières versions de python, un tampon de chaîne caché est créé lorsque vous concaténer des chaînes de caractères dans une boucle.

5 votes

@Seun : Oui, comme je l'ai dit, certaines langues vont optimiser, mais c'est une pratique risquée.

11voto

S.Lott Points 207588

"Comme la concaténation des chaînes de caractères a connu de grandes augmentations de performance..."

Si les performances comptent, c'est bon à savoir.

Cependant, les problèmes de performance que j'ai constatés ne se résument jamais à des opérations sur des chaînes de caractères. J'ai généralement eu des problèmes avec les E/S, le tri et O( n 2 ) étant les goulots d'étranglement.

Jusqu'à ce que les opérations sur les chaînes de caractères limitent les performances, je m'en tiendrai aux choses qui sont évidentes. La plupart du temps, il s'agit de substitution quand il s'agit d'une ligne ou moins, de concaténation quand cela a du sens, et d'un outil de template (comme Mako) quand c'est important.

10voto

Tim Lesher Points 3056

Ce que vous voulez concaténer/interpoler et la façon dont vous voulez formater le résultat doivent guider votre décision.

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

  • L'interpolation facilite la mise en forme 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 à mettre sous forme de chaîne.

Sachez également que Python 2.6 introduit une nouvelle version de l'interpolation de chaînes de caractères, appelée modèle de chaîne de caractères :

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

La modélisation des chaînes de caractères est destinée à remplacer un jour l'interpolation en %, mais cela ne se fera pas avant un certain temps, je pense.

0 votes

Eh bien, ça arrivera quand vous déciderez de passer à python 3.0. Voir également le commentaire de Peter pour le fait que vous pouvez de toute façon effectuer des substitutions nommées avec l'opérateur %.

0 votes

"La concaténation est utile lorsque vous n'avez pas un nombre fixe d'éléments à mettre en chaîne." -- Vous voulez dire une liste/un tableau ? Dans ce cas, ne pourriez-vous pas simplement les joindre () ?

0 votes

"Ne pourriez-vous pas simplement les joindre () ?" -- Oui (en supposant que vous voulez des séparateurs uniformes entre les éléments). Les compréhensions de listes et de générateurs fonctionnent très bien avec string.join.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X