Un anti-modèle courant en Python consiste à concaténer une séquence de chaînes en utilisant +
dans une boucle. C'est mauvais car l'interpréteur Python doit créer un nouvel objet chaîne pour chaque itération, et cela finit par prendre un temps quadratique. (Les versions récentes de CPython peuvent apparemment optimiser cela dans certains cas, mais d'autres implémentations ne le peuvent pas, donc les programmeurs sont découragés de se fier à cela.) ''.join
Est la bonne façon de le faire.
Cependant, j'ai entendu dire ( y compris ici sur Stack Overflow ) que vous ne devriez jamais, jamais utiliser +
pour la concaténation de chaînes, mais à la place toujours utiliser ''.join
une chaîne de format. Je ne comprends pas pourquoi c'est le cas si vous ne concaténez que deux chaînes. Si ma compréhension est correcte, cela ne devrait pas prendre de temps quadratique, et je pense que a + b
c'est plus propre et plus lisible que l'un ''.join((a, b))
ou l' autre '%s%s' % (a, b)
.
Est-ce une bonne pratique d'utiliser +
pour concaténer deux chaînes? Ou y a-t-il un problème dont je ne suis pas au courant?
+
c'est plus rapide ou plus lent? Et pourquoi?In [2]: %timeit "a"*80 + "b"*80
1000000 loops, best of 3: 356 ns per loop
In [3]: %timeit "%s%s" % ("a"*80, "b"*80)
1000000 loops, best of 3: 907 ns per loop
In [3]: %timeit "%s%s" % (a, b) 1000000 loops, best of 3: 590 ns per loop
In [4]: %timeit a + b 10000000 loops, best of 3: 147 ns per loop
__str__
. Voir ma réponse pour des exemples.Réponses:
Il n'y a rien de mal à concaténer deux chaînes avec
+
. En effet, c'est plus facile à lire que''.join([a, b])
.Vous avez raison cependant que concaténer plus de 2 chaînes avec
+
est une opération O (n ^ 2) (par rapport à O (n) pourjoin
) et devient donc inefficace. Cependant, cela n'a pas à voir avec l'utilisation d'une boucle. Mêmea + b + c + ...
est O (n ^ 2), la raison étant que chaque concaténation produit une nouvelle chaîne.CPython2.4 et les versions ultérieures essaient d'atténuer cela, mais il est toujours conseillé de l'utiliser
join
lors de la concaténation de plus de 2 chaînes.la source
.join
prend un itérable, donc les deux.join([a,b])
et.join((a,b))
sont valides.+
ou+=
dans la réponse acceptée (à partir de 2013) sur stackoverflow.com/a/12171382/378826 (de Lennart Regebro) même pour CPython 2.3+ et de ne choisir le modèle «ajouter / joindre» que si ce plus clair expose le idée de la solution du problème à portée de main.L'opérateur Plus est une solution parfaitement adaptée pour concaténer deux chaînes Python. Mais si vous continuez à ajouter plus de deux chaînes (n> 25), vous voudrez peut-être penser à autre chose.
''.join([a, b, c])
L'astuce est une optimisation des performances.la source
append()
chaînes dans une liste.n > 25
. Les humains ont besoin de points de référence pour commencer quelque part.L'hypothèse selon laquelle on ne devrait jamais, jamais utiliser + pour la concaténation de chaînes, mais à la place toujours utiliser «» .join peut être un mythe. Il est vrai que l'utilisation
+
crée des copies temporaires inutiles de l'objet chaîne immuable, mais l'autre fait non souvent cité est que l'appeljoin
dans une boucle ajouterait généralement la surcharge defunction call
. Prenons votre exemple.Créez deux listes, une à partir de la question SO liée et une autre
Permet de créer deux fonctions
UseJoin
etUsePlus
d'utiliser les fonctionnalitésjoin
et respectives+
.Permet d'exécuter timeit avec la première liste
Ils ont presque le même temps d'exécution.
Permet d'utiliser cProfile
Et il semble que l'utilisation de Join, entraîne des appels de fonction inutiles qui pourraient ajouter à la surcharge.
Revenons maintenant à la question. Doit-on décourager l'utilisation de
+
overjoin
dans tous les cas?Je crois que non, les choses doivent être prises en considération
Et bien sûr, dans une optimisation prématurée du développement, c'est mal.
la source
join
à l'intérieur de la boucle elle-même - plutôt la boucle génèrerait une séquence qui serait transmise pour se joindre.Lorsque vous travaillez avec plusieurs personnes, il est parfois difficile de savoir exactement ce qui se passe. Utiliser une chaîne de format au lieu de la concaténation peut éviter un désagrément particulier qui nous est arrivé une tonne de fois:
Disons qu'une fonction nécessite un argument et que vous l'écrivez en vous attendant à obtenir une chaîne:
Donc, cette fonction peut être utilisée assez souvent tout au long du code. Vos collègues savent peut-être exactement ce qu'il fait, mais ne sont pas nécessairement parfaitement à jour sur les éléments internes, et peuvent ne pas savoir que la fonction attend une chaîne. Et ainsi ils peuvent finir avec ceci:
Il n'y aurait aucun problème si vous utilisiez simplement une chaîne de format:
Il en va de même pour tous les types d'objets qui définissent
__str__
, qui peuvent également être transmis:Alors oui: si vous pouvez utiliser une chaîne de format, faites-le et profitez de ce que Python a à offrir.
la source
+
.zeta = u"a\xac\u1234\u20ac\U00008000"
- vous devez donc l'utiliserprint 'bar: ' + unicode(zeta)
pour vous assurer qu'il n'y a pas d'erreur.%s
le fait bien sans y penser, et est beaucoup plus court"bar: %s"
peuvent par exemple être traduits"zrb: %s br"
dans une autre langue. La%s
version fonctionnera simplement, mais la version concatée de chaînes deviendrait un désordre pour gérer tous les cas et vos traducteurs auraient maintenant deux traductions distinctes à traiterdef
.J'ai fait un test rapide:
et chronométré:
Il y a apparemment une optimisation pour le
a = a + b
cas. Il ne présente pas de temps O (n ^ 2) comme on pourrait le soupçonner.Donc au moins en termes de performances, l'utilisation
+
est bonne.la source
Selon la documentation Python, l'utilisation de str.join () vous donnera une cohérence des performances à travers diverses implémentations de Python. Bien que CPython optimise le comportement quadratique de s = s + t, d'autres implémentations Python peuvent ne pas le faire.
Types de séquence dans les documents Python (voir la note de bas de page [6])
la source
J'utilise ce qui suit avec python 3.8
la source
'' .join ([a, b]) est une meilleure solution que + .
Parce que le code doit être écrit d'une manière qui ne désavantage pas les autres implémentations de Python (PyPy, Jython, IronPython, Cython, Psyco, etc.)
form a + = b ou a = a + b est fragile même en CPython et n'est pas du tout présent dans les implémentations qui n'utilisent pas de refcounting (le comptage de références est une technique de stockage du nombre de références, pointeurs ou poignées vers un ressource comme un objet, un bloc de mémoire, un espace disque ou une autre ressource )
https://www.python.org/dev/peps/pep-0008/#programming-recommendations
la source
a += b
fonctionne dans toutes les implémentations de Python, c'est juste que sur certaines d'entre elles, cela prend un temps quadratique lorsqu'il est fait à l'intérieur d'une boucle ; la question portait sur la concaténation de chaînes en dehors d'une boucle.