Classe de chaîne Python comme StringBuilder en C #?

121

Existe-t-il une classe de chaîne en Python comme StringBuilderen C #?

icn
la source
6
Il s'agit d'un doublon de l' équivalent Python de Java StringBuffer . ATTENTION: Les réponses ici sont bien dépassées et sont en fait devenues trompeuses. Voir cette autre question pour des réponses plus pertinentes pour les versions Python modernes (certainement 2.7 et plus).
Jean-François Corbett

Réponses:

102

Il n'y a pas de corrélation biunivoque. Pour un très bon article, veuillez consulter Concaténation efficace de chaînes en Python :

Construire de longues chaînes dans le langage de programmation Python peut parfois entraîner un code d'exécution très lent. Dans cet article, j'étudie les performances de calcul de diverses méthodes de concaténation de chaînes.

Andrew Hare
la source
27
Notez que cet article a été écrit sur la base de Python 2.2. Les tests seraient probablement quelque peu différents dans une version moderne de Python (CPython optimise généralement avec succès la concaténation, mais vous ne voulez pas en dépendre dans un code important) et une expression de générateur dans laquelle il utilise une compréhension de liste mériterait d'être prise en considération .
Mike Graham
4
Il serait bon de tirer quelques faits saillants dans cet article, au moins quelques implémentations (pour éviter les problèmes de pourriture des liens).
jpmc26
3
Méthode 1: resultString + = appendString est le plus rapide selon les tests de @ Antoine-tran ci-dessous
Justas
5
Votre devis ne répond pas du tout à la question. Veuillez inclure les parties pertinentes dans votre réponse elle-même, afin de vous conformer aux nouvelles directives.
Fonder le procès de Monica le
27

J'ai utilisé le code d'Oliver Crow (lien donné par Andrew Hare) et je l'ai un peu adapté pour adapter Python 2.7.3. (en utilisant le package timeit). J'ai couru sur mon ordinateur personnel, Lenovo T61, 6 Go de RAM, Debian GNU / Linux 6.0.6 (squeeze).

Voici le résultat pour 10000 itérations:

méthode1: 0,0538418292999 secondes
taille du processus 4800 kb
méthode2: 0.22602891922 secondes
taille du processus 4960 kb
method3: 0,0605459213257 secondes
taille du processus 4980 kb
method4: 0,0544030666351 secondes
taille du processus 5536 kb
method5: 0,0551080703735 secondes
taille du processus 5272 kb
method6: 0,0542731285095 secondes
taille du processus 5512 kb

et pour 5.000.000 itérations (la méthode 2 a été ignorée car elle s'est déroulée trop lentement, comme pour toujours):

méthode1: 5.88603997231 secondes
taille du processus 37976 kb
méthode3: 8.40748500824 secondes
taille du processus 38024 kb
method4: 7.96380496025 secondes
taille du processus 321968 kb
method5: 8.03666186333 secondes
taille du processus 71720 kb
method6: 6.68192911148 secondes
taille du processus 38240 kb

Il est assez évident que les gars de Python ont fait du très bon travail pour optimiser la concaténation de chaînes, et comme l'a dit Hoare: "l'optimisation prématurée est la racine de tout le mal" :-)

Antoine-Tran
la source
2
Apparemment, Hoare n'accepte pas cela: hans.gerwitz.com/2004/08/12/…
Pimin Konstantin Kefaloukos
5
Il ne s'agit pas d'une optimisation prématurée pour éviter des optimisations fragiles et dépendantes de l'interprète. Si jamais vous souhaitez porter sur PyPy ou risquer de rencontrer l' un des nombreux cas d'échec subtils pour l'optimisation, faites les choses de la bonne manière.
Veedrac
1
Il semble que la méthode 1 soit plus facile à optimiser pour le compilateur.
mbomb007
25

Se fier aux optimisations du compilateur est fragile. Les repères liés dans la réponse acceptée et les chiffres donnés par Antoine-tran ne sont pas dignes de confiance. Andrew Hare fait l'erreur d'inclure un appel à reprdans ses méthodes. Cela ralentit toutes les méthodes de la même manière mais obscurcit la vraie pénalité dans la construction de la chaîne.

Utilisez join. C'est très rapide et plus robuste.

$ ipython3
Python 3.5.1 (default, Mar  2 2016, 03:38:02) 
IPython 4.1.2 -- An enhanced Interactive Python.

In [1]: values = [str(num) for num in range(int(1e3))]

In [2]: %%timeit
   ...: ''.join(values)
   ...: 
100000 loops, best of 3: 7.37 µs per loop

In [3]: %%timeit
   ...: result = ''
   ...: for value in values:
   ...:     result += value
   ...: 
10000 loops, best of 3: 82.8 µs per loop

In [4]: import io

In [5]: %%timeit
   ...: writer = io.StringIO()
   ...: for value in values:
   ...:     writer.write(value)
   ...: writer.getvalue()
   ...: 
10000 loops, best of 3: 81.8 µs per loop
GrantJ
la source
Oui, l' reprappel domine l'exécution, mais il n'est pas nécessaire de rendre l'erreur personnelle.
Alex Reinking
3
@AlexReinking désolé, rien de personnel ne voulait dire. Je ne sais pas ce qui vous a fait penser que c'était personnel. Mais si c'était l'utilisation de leurs noms, je les ai utilisés uniquement pour faire référence aux réponses de l'utilisateur (correspond aux noms d'utilisateur, je ne sais pas s'il existe un meilleur moyen).
GrantJ
1
bon exemple de synchronisation qui sépare les opérations d'initialisation et de concaténation des données
aiodintsov
19

Python a plusieurs choses qui remplissent des objectifs similaires:

  • Une façon courante de créer de grandes chaînes à partir de pièces consiste à agrandir une liste de chaînes et à la joindre lorsque vous avez terminé. Ceci est un idiome Python fréquemment utilisé.
    • Pour créer des chaînes incorporant des données avec mise en forme, vous devez effectuer la mise en forme séparément.
  • Pour l'insertion et la suppression au niveau des caractères, vous conserverez une liste de chaînes de longueur un. (Pour faire cela à partir d'une chaîne, vous appelez list(your_string). Vous pouvez également utiliser un UserString.MutableStringpour cela.
  • (c)StringIO.StringIO est utile pour les choses qui prendraient autrement un fichier, mais moins pour la construction générale de chaînes.
Mike Graham
la source
10

En utilisant la méthode 5 ci-dessus (The Pseudo File), nous pouvons obtenir de très bonnes performances et une très bonne flexibilité

from cStringIO import StringIO

class StringBuilder:
     _file_str = None

     def __init__(self):
         self._file_str = StringIO()

     def Append(self, str):
         self._file_str.write(str)

     def __str__(self):
         return self._file_str.getvalue()

l'utilise maintenant

sb = StringBuilder()

sb.Append("Hello\n")
sb.Append("World")

print sb
Thomas Watson
la source
-1

Il n'y a pas d'analogue explicite - je pense que vous devez utiliser des concaténations de chaînes (probablement optimisées comme dit précédemment) ou une classe tierce (je doute qu'elles soient beaucoup plus efficaces - les listes en python sont de type dynamique, donc pas de travail rapide char [] pour tampon comme je suppose). Les classes de type Stringbuilder ne sont pas une optimisation prématurée en raison de la fonctionnalité innée des chaînes dans de nombreux langages (immuabilité) - qui permet de nombreuses optimisations (par exemple, référencer le même tampon pour les tranches / sous-chaînes). Les classes de type Stringbuilder / stringbuffer / stringstream fonctionnent beaucoup plus rapidement que la concaténation de chaînes (produisant de nombreux petits objets temporaires qui nécessitent encore des allocations et un ramasse-miettes) et même des outils de formatage de chaîne de type printf, sans avoir besoin d'interpréter la surcharge du modèle de formatage qui est assez consommatrice pour beaucoup d'appels de format.

Cerveau
la source
-4

Si vous recherchez ici une méthode de concaténation de chaînes rapide en Python, vous n'avez pas besoin d'une classe StringBuilder spéciale. La concaténation simple fonctionne aussi bien sans la pénalité de performance vue en C #.

resultString = ""

resultString += "Append 1"
resultString += "Append 2"

Voir la réponse d' Antoine-tran pour les résultats de performance

Tout comme
la source