9
votes

Comment diviser une chaîne et rejoindre-le sans créer une liste intermédiaire en Python?

Dis que j'ai quelque chose comme ce qui suit: xxx

(c.-à-d. Délis à toutes les lignes commençant par # à partir de la chaîne multiligne src ) < p> src est très grand, donc je suppose que .split () créera une grande liste intermédiaire. Je peux changer la compréhension de la liste dans une expression génératrice, mais existe-t-il une sorte de "xsplit" que je peux utiliser pour ne travailler que sur une ligne à la fois? Mon hypothèse est-elle correcte? Quelle est la méthode efficace (mémoire) efficace de gérer cela?

Clarification : Ceci est né en raison de mon code à court de mémoire. Je sais qu'il y a des moyens de réécrire de manière entièrement que mon code pour travailler autour de cela, mais la question concerne Python: y a-t-il une version de Split () (ou un idiome équivalent) qui se comporte comme un générateur et donc ne fait pas de travail supplémentaire. Copie de src ?


5 commentaires

Que comptez-vous faire avec la grande chaîne d'entrée et la chaîne de résultat éventuellement plus petite après l'exercice? Laissez-les en mémoire à perpétuité? Supprimez la chaîne d'entrée et écrivez la chaîne de sortie dans un fichier?


Pourquoi avez-vous besoin d'avoir la grande chaîne en mémoire à la fin? Expliquez cela et peut-être que nous pouvons vous aider mieux.


Quelle erreur obtenez vous? Ou s'il n'y a pas d'erreur, alors pourquoi est-ce un problème? Pourquoi es-tu inquiet de sauver une ressource (mémoire) si bon marché et abondante?


Comme indiqué dans un commentaire ci-dessous, la réduction de l'utilisation de la mémoire avec les modifications minimales est l'objectif. Réécrivez le code pour que les données sur le disque soient mieux bien sûr, mais ce n'est pas une option actuelle. La chaîne SRC peut être supprimée, Dest est traité plus loin.


Pouvez-vous publier le résultat de len (src) et src.Count ('\ n') pour l'une des chaînes où il échoue avec un MemoryError ?


5 Réponses :


1
votes

Le problème est que les chaînes sont immuables dans Python, il sera donc très difficile de faire quoi que ce soit du tout sans stockage intermédiaire.


6 commentaires

Entendu. Ce que je veux faire, c'est de minimiser le stockage intermédiaire utilisé. Il y a SRC, puis une copie de celle faite par Split, puis une copie dans la compréhension de la liste, puis des devises. Deux fois plus de copies que théoriquement nécessaires.


Je m'attends à ce que le générateur vous économisera beaucoup pour très peu d'effort. Quelque chose d'au-delà est peut-être une optimisation prématurée?


Malheureusement, non, le processus est à court de mémoire ... Le générateur l'obtient de 3 copies, de même que Stringio, mais le générateur est certainement moins d'effort qui est meilleur.


@TOM: Si vous manquez de mémoire, vous ne devriez peut-être pas faire cette opération en mémoire en premier lieu? Vous pouvez plutôt stocker les données sur le disque et lire et l'écrire à la mode en continu - lire une ligne, écrire une ligne, lire une ligne, écrire une ligne, etc ... alors vous n'avez besoin que de quelques ko de mémoire pour le exploitation entière. Mais vous devriez mesurer la performance sinon vous ne devez que deviner et deviner la performance d'un programme simple est très, très difficile.


@Mark, vous avez probablement raison, mais j'espère éviter de réécrire ce morceau de code autant que possible. Réduire de moitié l'utilisation de la mémoire est probablement suffisamment bonne, 33% de moins à faire.


@TOM: "Réduire la consommation de la mémoire est probablement suffisamment bonne". Acheterait deux fois plus de mémoire être une option?



4
votes

Dans votre code existant, vous pouvez modifier la liste en une expression génératrice:

 9.38s   # Split then join with temporary list
 9.92s   # Split then join with generator
 8.60s   # Regular expression
64.56s   # StringIO


4 commentaires

Je suis après l'efficacité de la mémoire, pas de vitesse, en évitant les copies temporaires des données est l'objectif. L'expression génératrice aide un peu, mais il y a encore 3 copies créées (par mon estimation). Savez-vous comment Regex.Sub () mesure à cet égard?


@Tom: En outre, quel est exactement le problème ou l'erreur que vous obtenez avec votre code actuel? Si vous obtenez une exception, pouvez-vous poster la trace de votre question? Si votre code se comporte simplement de manière indésirable, pouvez-vous décrire plus en détail quel comportement indésirable spécifique vous voyez et pourquoi c'est un problème pour vous?


Je reçois un MemoryError Exception sur une ligne similaire à celle postée. Il n'y a pas d'autres informations pertinentes dans la trace.


Nice, j'étais sur le point d'écrire "Essayez re.sub", juste avec une expression différente: r '(# [^ \ n] *) (?: \ n | $)' . Res devrait créer la chaîne de sortie en une fois (au lieu de diviser et rejoindre).



5
votes
buffer = StringIO(src)
dest = "".join(line for line in buffer if line[:1]!="#")
Of course, this really makes the most sense if you use StringIO throughout.  It works mostly the same as files.  You can seek, read, write, iterate (as shown), etc.

4 commentaires

C'est plutôt bien. Ne fonctionnera pas pour des appels plus génériques pour diviser () si cela sera-t-il? L'utilisation d'un stringio au lieu de rejoindre est une option. Stringio fait-il une copie du tampon ou est-il référencé?


Non, je ne connais aucune scission qui retourne un itérateur. Je pense que Stringio en fait immédiatement une copie, car les chaînes sont immuables. Cela pourrait être copié-on-écrit, mais je ne le pense pas.


J'éviterais d'utiliser Str comme nom de variable.


Merci @mark. Je l'ai changé en SRC .



5
votes

Voici un moyen de faire un type de scission général en utilisant iTertools xxx

groupeby traite chaque caractère de SRC séparément, de sorte que la performance n'est probablement pas stellaire, mais elle évite de créer n'importe quel intermédiaire énormes structures de données

probablement mieux de dépenser quelques lignes et de créer un générateur xxx

re a une méthode appelée Finditer , qui pourrait être utilisé à cet effet trop xxx

comparer la performance est un exercice pour que l'op d'essayer sur les données réelles < / p>


2 commentaires

Taper sur! Merci, re.finditer () est le genre de chose que je pensais. Votre fonction isplit () est bonne aussi, mais j'espérais que ce serait dans la bibliothèque standard et j'espère avoir une implémentation C ou quelque chose comme ça.


Oui et non. Oui: Le comportement convient aux exigences de l'OP. Non: il n'implante pas le comportement de str.split en produisant une finale '' lorsque s.endswith (t) - il se comporte plus comme Str.splitlines ( Faux).



2
votes

Si je comprends votre question sur "Appels plus génériques à Split ()" correctement, vous pouvez utiliser re.finditer , comme: xxx

ici vous peut remplacer l'expression régulière par quelque chose de plus sophistiqué.


0 commentaires