131 votes

Aucune raison de ne pas utiliser + pour concaténer deux chaînes de caractères?

D'un commun antipattern en Python est pour concaténer une séquence de chaînes à l'aide d' + dans une boucle. C'est mauvais parce que l'interpréteur Python est de créer un nouvel objet string pour chaque itération, et il finit par prendre quadratique du temps. (Les versions récentes de Disponible peut apparemment optimiser ce, dans certains cas, mais d'autres implémentations ne peut pas, de sorte que les programmeurs sont découragés de s'en remettre sur ce point.) ''.join est la bonne façon de le faire.

Cependant, j'ai entendu dire (y compris ici sur Stack Overflow) que vous devriez jamais, jamais utiliser + pour la concaténation de chaîne, mais au lieu de toujours utiliser ''.join ou une chaîne de format. Je ne comprends pas pourquoi c'est le cas si vous êtes seulement à la concaténation de deux chaînes. Si ma compréhension est correcte, il ne devrait pas prendre quadratique du temps, et je pense que a + b est plus propre et plus lisible que soit ''.join((a, b)) ou '%s%s' % (a, b).

Il est de bonne pratique d'utiliser + pour concaténer deux chaînes de caractères? Ou est-il un problème que je ne suis pas au courant?

125voto

ggozad Points 8910

Il n'y a rien de mal à la concaténation de deux chaînes de caractères avec des +. En effet, il est plus facile de lire que d' ''.join(a, b).

Vous avez raison, bien que la concaténation de plus de 2 chaînes avec + est un O(n^2) opération (par rapport à O(n) join) et devient donc inefficace. Toutefois, cela n'a pas à faire avec l'aide d'une boucle. Même a + b + c + ... O(n^2), la raison étant que chaque concaténation produit une nouvelle chaîne.

CPython2.4 et abone essayer d'atténuer la situation, mais il est toujours conseillé d'utiliser join lors de la concaténation de plus de 2 cordes.

49voto

Mikko Ohtamaa Points 20940

Plus l'opérateur est parfaitement bien la solution pour concaténer deux Python cordes. Mais si vous continuez à ajouter plus de deux chaînes (n > 25) , vous pourriez envie de penser à autre chose.

''.join([a, b, c]) astuce consiste en l'optimisation de la performance.

8voto

Abhijit Points 24122

L'hypothèse que l'on ne devrait jamais utiliser + pour la concaténation de chaîne, mais au lieu de toujours utiliser ".jointure peut être un mythe. Il est vrai que l'utilisation d' + créer des copies temporaires de immuable objet de type string, mais l'autre n'est pas souvent cité est un fait que l'appelant join dans une boucle serait généralement ajouter la surcharge d' function call. Prenons votre exemple.

Créer deux listes, l'une de la de rattachement question et une autre d'un plus fabriqué

>>> myl1 = ['A','B','C','D','E','F']
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)]

Permet de créer deux fonctions, UseJoin et UsePlus d'utilisation respectifs join et + fonctionnalités.

>>> def UsePlus():
    return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)]

>>> def UseJoin():
    [''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)]

Permet d'exécuter timeit avec la première liste

>>> myl=myl1
>>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus")
>>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
2.48 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
2.61 usec/pass
>>> 

Ils ont presque le même runtime.

Permet d'utiliser cProfile

>>> myl=myl2
>>> cProfile.run("UsePlus()")
         5 function calls in 0.001 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    0.001    0.001 <pyshell#1376>:1(UsePlus)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


>>> cProfile.run("UseJoin()")
         5005 function calls in 0.029 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.015    0.015    0.029    0.029 <pyshell#1388>:1(UseJoin)
        1    0.000    0.000    0.029    0.029 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     5000    0.014    0.000    0.014    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {range}

Et il semble que l'utilisation de Joindre, les résultats des appels de fonction qui pourrait ajouter à la surcharge.

Maintenant, je reviens à la question. Doit-on décourager l'utilisation de + sur join dans tous les cas?

Je ne le pense pas, les choses devraient être pris en considération

  1. Longueur de la Chaîne en Question
  2. Pas d'Opération de Concaténation.

Et-cours dans un développement pré-maturité d'optimisation est le mal.

7voto

Izkata Points 3634

Lorsque vous travaillez avec plusieurs personnes, il est parfois difficile de savoir exactement ce qui se passe. En utilisant un format de chaîne de caractères au lieu de la concaténation peut éviter que l'un particulier de la gêne qui s'est passé toute une tonne de temps à nous:

Dire, une fonction nécessite un argument, et vous l'écrivez, s'attendant à obtenir une chaîne de caractères:

In [1]: def foo(zeta):
   ...:     print 'bar: ' + zeta

In [2]: foo('bang')
bar: bang

Donc, cette fonction peut être utilisée assez souvent tout au long du code. Vos collègues peuvent savoir exactement ce qu'il fait, mais pas nécessairement complètement à la vitesse à l'intérieur, et qui ne savent pas que la fonction attend une chaîne de caractères. Et donc, ils peuvent se retrouver avec ceci:

In [3]: foo(23)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

/home/izkata/<ipython console> in foo(zeta)

TypeError: cannot concatenate 'str' and 'int' objects

Il n'y aurait pas de problème si vous venez d'utiliser une chaîne de format:

In [1]: def foo(zeta):
   ...:     print 'bar: %s' % zeta
   ...:     
   ...:     

In [2]: foo('bang')
bar: bang

In [3]: foo(23)
bar: 23

Le même est vrai pour tous les types d'objets qui définissent __str__, ce qui peut être transmis en tant que bien:

In [1]: from datetime import date

In [2]: zeta = date(2012, 4, 15)

In [3]: print 'bar: ' + zeta
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

TypeError: cannot concatenate 'str' and 'datetime.date' objects

In [4]: print 'bar: %s' % zeta
bar: 2012-04-15

Donc oui: Si vous pouvez utiliser une chaîne de format de le faire et profiter de ce Python a à offrir.

3voto

Michael Slade Points 9128

J'ai fait un test rapide:

import sys

str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx\n"

for i in range(int(sys.argv[1])):
    str = str + e

et chronométré:

mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  8000000
8000000 times

real    0m2.165s
user    0m1.620s
sys     0m0.540s
mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  16000000
16000000 times

real    0m4.360s
user    0m3.480s
sys     0m0.870s

Apparemment, il y a une optimisation pour l' a = a + b des cas. Il ne présente pas de O(n^2) le temps comme on pourrait le penser.

Ainsi, au moins en termes de performances, à l'aide de + est fine.

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