string.split()
renvoie une instance de liste . Existe-t-il une version qui renvoie un générateur à la place? Y a-t-il des raisons de ne pas avoir une version générateur?
113
string.split()
renvoie une instance de liste . Existe-t-il une version qui renvoie un générateur à la place? Y a-t-il des raisons de ne pas avoir une version générateur?
split
la chaîne et a ensuite renvoyé un générateur travaillant sur le résultat desplit
. Cela m'a fait réfléchir s'il y avait un moyensplit
de retourner un générateur pour commencer.Réponses:
Il est très probable que cela
re.finditer
utilise une surcharge de mémoire assez minime.Démo:
edit: Je viens de confirmer que cela prend une mémoire constante dans python 3.2.1, en supposant que ma méthodologie de test était correcte. J'ai créé une chaîne de très grande taille (1 Go environ), puis j'ai parcouru l'itérable avec une
for
boucle (PAS une compréhension de liste, ce qui aurait généré de la mémoire supplémentaire). Cela n'a pas entraîné une augmentation notable de la mémoire (c'est-à-dire que s'il y avait une augmentation de la mémoire, c'était bien moins que la chaîne de 1 Go).la source
a_string.split("delimiter")
?str.split()
n'accepte pas les expressions régulières, c'est cere.split()
que vous pensez ...La façon la plus efficace pour moi d'en écrire un en utilisant le
offset
paramètre de lastr.find()
méthode. Cela évite une utilisation intensive de la mémoire et la surcharge d'une expression rationnelle lorsqu'elle n'est pas nécessaire.[modifier 2016-8-2: mis à jour ceci pour prendre en charge facultativement les séparateurs d'expression régulière]
Cela peut être utilisé comme vous le souhaitez ...
Bien qu'il y ait un peu de recherche de coût dans la chaîne à chaque fois que find () ou slicing est effectué, cela devrait être minime car les chaînes sont représentées comme des tableaux contingents en mémoire.
la source
Il s'agit d'une version génératrice de
split()
implémentée viare.search()
qui n'a pas le problème d'allouer trop de sous-chaînes.EDIT: Correction de la gestion des espaces blancs environnants si aucun caractère de séparation n'est donné.
la source
re.finditer
?J'ai fait quelques tests de performance sur les différentes méthodes proposées (je ne les répéterai pas ici). Quelques résultats:
str.split
(par défaut = 0,3461570239996945re.finditer
(réponse de ninjagecko) = 0,698872097000276str.find
(une des réponses d'Eli Collins) = 0,7230395330007013itertools.takewhile
(Réponse d'Ignacio Vazquez-Abrams) = 2.023023967998597str.split(..., maxsplit=1)
récursivité = N / A †† Les réponses de récursivité (
string.split
avecmaxsplit = 1
) ne se terminent pas dans un temps raisonnable, étant donné lastring.split
vitesse, elles peuvent mieux fonctionner sur des chaînes plus courtes, mais je ne vois pas le cas d'utilisation des chaînes courtes où la mémoire n'est de toute façon pas un problème.Testé
timeit
sur:Cela soulève une autre question de savoir pourquoi
string.split
est tellement plus rapide malgré son utilisation de la mémoire.la source
Voici ma mise en œuvre, qui est beaucoup, beaucoup plus rapide et plus complète que les autres réponses ici. Il dispose de 4 sous-fonctions distinctes pour différents cas.
Je vais simplement copier la docstring de la
str_split
fonction principale :Divisez la chaîne
s
par le reste des arguments, en omettant éventuellement les parties vides (empty
argument mot-clé en est responsable). C'est une fonction de générateur.Lorsqu'un seul délimiteur est fourni, la chaîne est simplement divisée par lui.
empty
est alorsTrue
par défaut.Lorsque plusieurs délimiteurs sont fournis, la chaîne est divisée par les séquences les plus longues possibles de ces délimiteurs par défaut, ou, si elle
empty
est définie surTrue
, des chaînes vides entre les délimiteurs sont également incluses. Notez que les délimiteurs dans ce cas ne peuvent être que des caractères uniques.Lorsqu'aucun délimiteur n'est fourni,
string.whitespace
est utilisé, donc l'effet est le même questr.split()
, sauf que cette fonction est un générateur.Cette fonction fonctionne dans Python 3, et un correctif simple, mais assez laid, peut être appliqué pour le faire fonctionner dans les versions 2 et 3. Les premières lignes de la fonction doivent être remplacées par:
la source
Non, mais il devrait être assez facile d'en écrire un avec
itertools.takewhile()
.ÉDITER:
Implémentation très simple, à moitié cassée:
la source
takeWhile
. Qu'est-ce qui serait bonpredicate
pour diviser une chaîne en mots (par défautsplit
) en utilisanttakeWhile()
?string.whitespace
.'abc<def<>ghi<><>lmn'.split('<>') == ['abc<def', 'ghi', '', 'lmn']
Je ne vois aucun avantage évident à une version génératrice desplit()
. L'objet générateur va devoir contenir toute la chaîne à parcourir afin que vous n'économisiez pas de mémoire en ayant un générateur.Si vous vouliez en écrire un, ce serait assez simple:
la source
id()
m'a mis raison. Et évidemment, comme les chaînes sont immuables, vous n'avez pas à vous soucier du fait que quelqu'un change la chaîne d'origine pendant que vous l'itérez.J'ai écrit une version de la réponse de @ ninjagecko qui se comporte plus comme string.split (c'est-à-dire que des espaces sont délimités par défaut et vous pouvez spécifier un délimiteur).
Voici les tests que j'ai utilisés (en python 3 et python 2):
Le module regex de python dit qu'il fait «ce qu'il faut» pour les espaces unicode, mais je ne l'ai pas testé.
Également disponible sous forme de résumé .
la source
Si vous souhaitez également pouvoir lire un itérateur (et en renvoyer un), essayez ceci:
Usage
la source
more_itertools.split_at
propose un analogiquestr.split
pour les itérateurs.more_itertools
est un package tiers.la source
itertools.chain
et évaluer les résultats en utilisant une compréhension de liste. En fonction du besoin et de la demande, je peux poster un exemple.Je voulais montrer comment utiliser la solution find_iter pour renvoyer un générateur pour des délimiteurs donnés, puis utiliser la recette par paires d'itertools pour créer une itération suivante précédente qui obtiendra les mots réels comme dans la méthode de fractionnement d'origine.
Remarque:
la source
Méthode la plus stupide, sans regex / itertools:
la source
la source
[f[j:i]]
et nonf[j:i]
?voici une réponse simple
la source