431 votes

Quelle est la méthode préférée pour concaténer une chaîne de caractères en Python ?

Étant donné que l'interface de Python string ne peut pas être modifié, je me demandais comment concaténer une chaîne de manière plus efficace ?

Je peux écrire comme ça :

s += stringfromelsewhere

ou comme ça :

s = []

s.append(somestring)

# later

s = ''.join(s)

En rédigeant cette question, j'ai trouvé un bon article traitant du sujet.

http://www.skymind.com/~ocrow/python_string/

Mais c'est dans Python 2.x., donc la question serait de savoir si quelque chose a changé dans Python 3 ?

1 votes

496voto

Lennart Regebro Points 52510

El meilleur Pour ajouter une chaîne de caractères à une variable de type chaîne de caractères, il faut utiliser la méthode suivante + ou += . C'est parce qu'il est lisible et rapide. Ils sont également tout aussi rapides, celui que vous choisissez est une question de goût, ce dernier est le plus courant. Voici les timings avec le timeit module :

a = a + b:
0.11338996887207031
a += b:
0.11040496826171875

Cependant, ceux qui recommandent d'avoir des listes et d'y ajouter des éléments, puis de joindre ces listes, le font parce que l'ajout d'une chaîne à une liste est vraisemblablement très rapide par rapport à l'extension d'une chaîne. Et cela peut être vrai, dans certains cas. Voici, par exemple, un million d'ajouts d'une chaîne d'un caractère, d'abord à une chaîne, puis à une liste :

a += b:
0.10780501365661621
a.append(b):
0.1123361587524414

OK, il s'avère que même lorsque la chaîne résultante compte un million de caractères, l'ajout est toujours plus rapide.

Essayons maintenant d'ajouter cent mille fois une chaîne de mille caractères :

a += b:
0.41823482513427734
a.append(b):
0.010656118392944336

La chaîne finale finit donc par faire environ 100 Mo. C'est assez lent, l'ajout à une liste est beaucoup plus rapide. Ce temps n'inclut pas le processus final a.join() . Alors, combien de temps cela prendrait-il ?

a.join(a):
0.43739795684814453

Oups. Il s'avère que même dans ce cas, append/join est plus lent.

Alors, d'où vient cette recommandation ? De Python 2 ?

a += b:
0.165287017822
a.append(b):
0.0132720470428
a.join(a):
0.114929914474

Eh bien, append/join est marginalement plus rapide là si vous utilisez des chaînes extrêmement longues (ce qui n'est généralement pas le cas, qu'est-ce que vous auriez d'une chaîne de 100 Mo de mémoire).

Mais le véritable argument décisif est Python 2.3. Où je ne vous montrerai même pas les chronologies, car il est si lent qu'il n'a pas encore terminé. Ces tests prennent soudainement minutes . Sauf pour l'append/join, qui est tout aussi rapide que sous les Python ultérieurs.

Yup. La concaténation de chaînes était très lente en Python à l'âge de pierre. Mais sur 2.4, elle ne l'est plus (ou du moins Python 2.4.7), donc la recommandation d'utiliser append/join est devenue obsolète en 2008, lorsque Python 2.3 a cessé d'être mis à jour, et vous auriez dû cesser de l'utiliser :-)

(Mise à jour : il s'est avéré, lorsque j'ai effectué les tests plus attentivement, que l'utilisation de + y += est plus rapide pour deux chaînes de caractères sous Python 2.3 également. La recommandation d'utiliser ''.join() doit être un malentendu)

Cependant, il s'agit de CPython. D'autres implémentations peuvent avoir d'autres préoccupations. Et ce n'est qu'une raison de plus pour laquelle l'optimisation prématurée est la racine de tous les maux. N'utilisez pas une technique supposée "plus rapide" sans l'avoir d'abord mesurée.

Par conséquent, la "meilleure" version pour faire la concaténation de chaînes de caractères est d'utiliser + ou +=. . Et si cela s'avère lent pour vous, ce qui est plutôt improbable, alors faites autre chose.

Alors pourquoi est-ce que j'utilise beaucoup d'append/join dans mon code ? Parce que parfois, c'est plus clair. Surtout quand ce que vous devez concaténer doit être séparé par des espaces, des virgules ou des retours à la ligne.

11 votes

Si vous avez plusieurs chaînes de caractères (n > 10) "".join(list_of_strings) est toujours plus rapide

0 votes

13 votes

La raison pour laquelle += est rapide est qu'il y a un hack de performance dans cpython si le refcount est 1 - il s'effondre sur à peu près toutes les autres implémentations de python (à l'exception d'une construction de pypy configurée de manière assez spéciale).

57voto

jdi Points 38029

Si vous concaténiez un grand nombre de valeurs, alors aucun des deux. L'ajout d'une liste est coûteux. Vous pouvez utiliser StringIO pour cela. Surtout si vous la construisez sur un grand nombre d'opérations.

from cStringIO import StringIO
# python3:  from io import StringIO

buf = StringIO()

buf.write('foo')
buf.write('foo')
buf.write('foo')

buf.getvalue()
# 'foofoofoo'

Si vous avez déjà une liste complète qui vous a été retournée par une autre opération, il suffit d'utiliser la fonction ''.join(aList)

Extrait de la FAQ python : Quel est le moyen le plus efficace de concaténer plusieurs chaînes de caractères ensemble ?

Les objets str et bytes sont immuables, c'est pourquoi la concaténation de plusieurs objets chaînes de caractères est inefficace car chaque concaténation crée un nouvel objet. nouvel objet. Dans le cas général, le coût total de l'exécution est quadratique en la longueur totale de la chaîne.

Pour accumuler de nombreux objets str, l'idiome recommandé est de les placer dans une liste et d'appeler str.join() à la fin :

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)

(un autre idiome raisonnablement efficace est d'utiliser io.StringIO)

Pour accumuler de nombreux objets octets, l'idiome recommandé est d'étendre un fichier bytearray en utilisant la concaténation sur place (l'opérateur +=) :

result = bytearray()
for b in my_bytes_objects:
    result += b

Edit : J'ai été stupide et j'ai collé les résultats à l'envers, faisant croire que l'ajout à une liste était plus rapide que cStringIO. J'ai également ajouté des tests pour bytearray/str concat, ainsi qu'une deuxième série de tests utilisant une liste plus grande avec des chaînes plus grandes. (python 2.7.3)

Exemple de test ipython pour les grandes listes de chaînes de caractères

try:
    from cStringIO import StringIO
except:
    from io import StringIO

source = ['foo']*1000

%%timeit buf = StringIO()
for i in source:
    buf.write(i)
final = buf.getvalue()
# 1000 loops, best of 3: 1.27 ms per loop

%%timeit out = []
for i in source:
    out.append(i)
final = ''.join(out)
# 1000 loops, best of 3: 9.89 ms per loop

%%timeit out = bytearray()
for i in source:
    out += i
# 10000 loops, best of 3: 98.5 µs per loop

%%timeit out = ""
for i in source:
    out += i
# 10000 loops, best of 3: 161 µs per loop

## Repeat the tests with a larger list, containing
## strings that are bigger than the small string caching 
## done by the Python
source = ['foo']*1000

# cStringIO
# 10 loops, best of 3: 19.2 ms per loop

# list append and join
# 100 loops, best of 3: 144 ms per loop

# bytearray() +=
# 100 loops, best of 3: 3.8 ms per loop

# str() +=
# 100 loops, best of 3: 5.11 ms per loop

3 votes

cStringIO n'existe pas dans Py3. Utilisez io.StringIO à la place.

4 votes

Quant à la raison pour laquelle l'ajout répété à une chaîne de caractères peut être coûteux : joelonsoftware.com/articles/fog0000000319.html

0 votes

Attendez quoi ? quand vous avez dit "appending a list [is expensive]", vous vouliez dire "appending a string", non ?

23voto

badslacks Points 21

L'utilisation de la concaténation de chaînes de caractères par '+' est LA PIRE méthode de concaténation en termes de stabilité et de mise en œuvre croisée, car elle ne prend pas en charge toutes les valeurs. Norme PEP8 décourage cela et encourage l'utilisation de format(), join() et append() pour une utilisation à long terme.

Comme cité dans la section "Recommandations de programmation" :

Par exemple, ne comptez pas sur l'implémentation efficace de CPython de la concaténation de chaînes de caractères en place pour les instructions de la forme a += b ou a = a + b. Cette optimisation est fragile même dans CPython (elle ne fonctionne que pour certains types) et n'est pas du tout présente dans les implémentations qui n'utilisent pas le refcounting. Dans les parties de la bibliothèque sensibles aux performances, la forme ''.join() devrait être utilisée à la place. Cela garantira que la concaténation se produit en temps linéaire dans les différentes implémentations.

7 votes

Un lien de référence aurait été bienvenu :)

6 votes

Quelle situation ridicule. C'est l'une des premières choses que l'on apprend aux gens à faire, et voilà que les sorciers de la tour d'ivoire publient un PEP qui décourage cette pratique parce qu'elle est fragile.

7voto

MRAB Points 9855

La méthode recommandée est toujours d'utiliser l'append et le join.

1 votes

Comme vous le voyez dans ma réponse, cela dépend du nombre de chaînes que vous concaténerez. J'ai fait quelques calculs de temps à ce sujet (voir la conférence dont j'ai donné le lien dans les commentaires de ma réponse) et, en général, à moins que le nombre de chaînes soit supérieur à dix, il faut utiliser +.

1 votes

PEP8 en fait mention ( python.org/dev/peps/pep-0008/#programming-recommendations ). Le raisonnement est que si CPython dispose d'optimisations spéciales pour la concaténation de chaînes de caractères avec +=, d'autres implémentations peuvent ne pas en disposer.

5voto

Levon Points 34085

Bien qu'un peu daté, Codez comme un pythoniste : Python idiomatique recommande join() sur + dans cette section . Comme le fait PythonSpeedPerformanceTips dans sa section sur concaténation de chaînes de caractères avec l'avertissement suivant :

L'exactitude de cette section est contestée en ce qui concerne les versions ultérieures. versions ultérieures de Python. Dans CPython 2.5, la concaténation des chaînes est assez rapide, bien que cela puisse ne pas s'appliquer également à d'autres implémentations d'autres implémentations Python. Voir ConcatenationTestCode pour une discussion.

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