Sur une note latérale, j'ai trouvé que la list()fonction itérera à travers son argument (un itérable). Ainsi, en appelant list()deux fois le même itérable (par exemple le résultat de zip()), vous obtiendrez une liste vide au deuxième appel!
theaws.blog
Réponses:
84
Je vois de nombreuses réponses suggérant itertools.tee , mais cela ignore un avertissement crucial dans la documentation à ce sujet:
Cet itertool peut nécessiter un stockage auxiliaire important (en fonction de la quantité de données temporaires à stocker). En général, si un itérateur utilise la plupart ou toutes les données avant qu'un autre itérateur ne démarre, il est plus rapide à utiliser list()au lieu de tee().
Fondamentalement, teeest conçu pour les situations où deux (ou plus) clones d'un itérateur, tout en "se désynchronisant" l'un avec l'autre, ne le font pas de beaucoup - plutôt, disent-ils dans le même "voisinage" (un quelques éléments derrière ou devant les uns des autres). Ne convient pas au problème de l'OP de "refaire depuis le début".
L = list(DictReader(...))d'autre part est parfaitement adapté, tant que la liste des dictionnaires peut tenir confortablement dans la mémoire. Un nouvel «itérateur dès le départ» (très léger et peu encombrant) peut être créé à tout moment avec iter(L), et utilisé en partie ou en totalité sans affecter les nouveaux ou existants; d'autres modèles d'accès sont également facilement disponibles.
Comme plusieurs réponses l'ont fait remarquer à juste titre, dans le cas spécifique de csvvous pouvez également .seek(0)l'objet de fichier sous-jacent (un cas assez particulier). Je ne suis pas sûr que ce soit documenté et garanti, bien que cela fonctionne actuellement; cela ne vaudrait probablement la peine de considérer que pour les fichiers csv vraiment énormes, dans lesquels listje recommande car l'approche générale aurait une empreinte mémoire trop importante.
Ensuite, vous pourrez obtenir la ligne suivante avec reader.next(), qui devrait afficher
{'a':1,'b':2,'c':3,'d':4}
l'utiliser à nouveau produira
{'a':2,'b':3,'c':4,'d':5}
Cependant, à ce stade, si vous utilisez blah.seek(0), la prochaine fois que vous appelez, reader.next()vous obtiendrez
{'a':1,'b':2,'c':3,'d':4}
encore.
Cela semble être la fonctionnalité que vous recherchez. Je suis sûr qu'il y a quelques astuces associées à cette approche dont je ne suis pas au courant cependant. @Brian a suggéré de créer simplement un autre DictReader. Cela ne fonctionnera pas si votre premier lecteur est à mi-chemin de la lecture du fichier, car votre nouveau lecteur aura des clés et des valeurs inattendues où que vous soyez dans le fichier.
C'est ce que ma théorie m'a dit, agréable de voir que ce que je pensais devrait arriver, le fait.
Wayne Werner
@Wilduck: le comportement que vous décrivez avec une autre instance de DictReader ne se produira pas si vous créez un nouveau descripteur de fichier et que vous le transmettez au deuxième DictReader, n'est-ce pas?
Si vous avez deux gestionnaires de fichiers, ils se comporteront indépendamment, oui.
Wilduck
24
Le protocole d'itérateur de Python est très simple et ne fournit qu'une seule méthode ( .next()ou __next__()), et aucune méthode pour réinitialiser un itérateur en général.
Le modèle courant consiste à créer à nouveau un nouvel itérateur en utilisant la même procédure.
Si vous souhaitez "enregistrer" un itérateur afin de pouvoir revenir à son début, vous pouvez également bifurquer l'itérateur en utilisant itertools.tee
Bien que votre analyse de la méthode .next () soit probablement correcte, il existe un moyen assez simple d'obtenir ce que l'op demande.
Wilduck
2
@Wilduck: Je vois que votre réponse. Je viens de répondre à la question de l'itérateur, et je n'ai aucune idée du csvmodule. Espérons que les deux réponses seront utiles à l'affiche originale.
u0b34a0f6ae
Strictement, le protocole itérateur nécessite également __iter__. Autrement dit, les itérateurs doivent également être itérables.
Steve Jessop
11
Oui , si vous utilisez numpy.nditerpour construire votre itérateur.
Il y a un bogue dans l'utilisation .seek(0)comme préconisé par Alex Martelli et Wilduck ci-dessus, à savoir que le prochain appel à .next()vous donnera un dictionnaire de votre ligne d'en-tête sous la forme de {key1:key1, key2:key2, ...}. Le travail autour consiste à suivre file.seek(0)avec un appel à reader.next()pour se débarrasser de la ligne d'en-tête.
Donc, votre code ressemblerait à ceci:
f_in = open('myfile.csv','r')
reader = csv.DictReader(f_in)for record in reader:if some_condition:# reset reader to first row of data on 2nd line of file
f_in.seek(0)
reader.next()continue
do_something(record)
C'est peut-être orthogonal à la question d'origine, mais on pourrait envelopper l'itérateur dans une fonction qui retourne l'itérateur.
def get_iter():return iterator
Pour réinitialiser l'itérateur, il suffit de rappeler la fonction. Ceci est bien sûr trivial si la fonction lorsque ladite fonction ne prend aucun argument.
Dans le cas où la fonction nécessite des arguments, utilisez functools.partial pour créer une fermeture qui peut être passée à la place de l'itérateur d'origine.
Ici, a DictReaderest enveloppé dans un seekableobjet (1) et avancé (2). La seek()méthode est utilisée pour réinitialiser / rembobiner l'itérateur à la position 0 (3).
Remarque: la consommation de mémoire augmente avec l'itération, alors méfiez-vous en appliquant cet outil à des fichiers volumineux, comme indiqué dans la documentation .
Bien qu'il n'y ait pas de réinitialisation d'itérateur, le module "itertools" de python 2.6 (et plus tard) a quelques utilitaires qui peuvent y aider. L'un d'eux est le «tee» qui peut faire plusieurs copies d'un itérateur, et mettre en cache les résultats de celui qui avance, de sorte que ces résultats soient utilisés sur les copies. Je vais répondre à vos objectifs:
>>>def printiter(n):...for i in xrange(n):...print"iterating value %d"% i
...yield i
>>>from itertools import tee
>>> a, b = tee(printiter(5),2)>>> list(a)
iterating value 0
iterating value 1
iterating value 2
iterating value 3
iterating value 4[0,1,2,3,4]>>> list(b)[0,1,2,3,4]
J'ai déjà eu le même problème. Après avoir analysé mon code, j'ai réalisé que tenter de réinitialiser l'itérateur à l'intérieur des boucles augmentait légèrement la complexité temporelle et rendait également le code un peu moche.
Solution
Ouvrez le fichier et enregistrez les lignes dans une variable en mémoire.
# initialize list of rows
rows =[]# open the file and temporarily name it as 'my_file'with open('myfile.csv','rb')as my_file:# set up the reader using the opened file
myfilereader = csv.DictReader(my_file)# loop through each row of the readerfor row in myfilereader:# add the row to the list of rows
rows.append(row)
Vous pouvez désormais parcourir les lignes n'importe où dans votre portée sans avoir à utiliser d'itérateur.
J'arrive à ce même problème - bien que j'aime la tee()solution, je ne sais pas quelle sera la taille de mes fichiers et les avertissements de mémoire concernant la consommation de l'un avant l'autre me découragent d'adopter cette méthode.
Au lieu de cela, je crée une paire d'itérateurs en utilisant des iter()instructions, et en utilisant le premier pour mon passage initial, avant de passer au second pour l'exécution finale.
Donc, dans le cas d'un lecteur de dict, si le lecteur est défini en utilisant:
d = csv.DictReader(f, delimiter=",")
Je peux créer une paire d'itérateurs à partir de cette "spécification" - en utilisant:
d1, d2 = iter(d), iter(d)
Je peux ensuite exécuter mon code de 1ère passe d1, en sachant que le deuxième itérateur d2a été défini à partir de la même spécification racine.
Je n'ai pas testé cela de manière exhaustive, mais cela semble fonctionner avec des données factices.
list()
fonction itérera à travers son argument (un itérable). Ainsi, en appelantlist()
deux fois le même itérable (par exemple le résultat dezip()
), vous obtiendrez une liste vide au deuxième appel!Réponses:
Je vois de nombreuses réponses suggérant itertools.tee , mais cela ignore un avertissement crucial dans la documentation à ce sujet:
Fondamentalement,
tee
est conçu pour les situations où deux (ou plus) clones d'un itérateur, tout en "se désynchronisant" l'un avec l'autre, ne le font pas de beaucoup - plutôt, disent-ils dans le même "voisinage" (un quelques éléments derrière ou devant les uns des autres). Ne convient pas au problème de l'OP de "refaire depuis le début".L = list(DictReader(...))
d'autre part est parfaitement adapté, tant que la liste des dictionnaires peut tenir confortablement dans la mémoire. Un nouvel «itérateur dès le départ» (très léger et peu encombrant) peut être créé à tout moment aveciter(L)
, et utilisé en partie ou en totalité sans affecter les nouveaux ou existants; d'autres modèles d'accès sont également facilement disponibles.Comme plusieurs réponses l'ont fait remarquer à juste titre, dans le cas spécifique de
csv
vous pouvez également.seek(0)
l'objet de fichier sous-jacent (un cas assez particulier). Je ne suis pas sûr que ce soit documenté et garanti, bien que cela fonctionne actuellement; cela ne vaudrait probablement la peine de considérer que pour les fichiers csv vraiment énormes, dans lesquelslist
je recommande car l'approche générale aurait une empreinte mémoire trop importante.la source
list()
pour mettre en cache le multipassage sur un csvreader sur un fichier de 5 Mo voit mon exécution passer de ~ 12secs à ~ 0.5s.Si vous avez un fichier csv nommé 'blah.csv' qui ressemble à
vous savez que vous pouvez ouvrir le fichier pour le lire et créer un DictReader avec
Ensuite, vous pourrez obtenir la ligne suivante avec
reader.next()
, qui devrait afficherl'utiliser à nouveau produira
Cependant, à ce stade, si vous utilisez
blah.seek(0)
, la prochaine fois que vous appelez,reader.next()
vous obtiendrezencore.
Cela semble être la fonctionnalité que vous recherchez. Je suis sûr qu'il y a quelques astuces associées à cette approche dont je ne suis pas au courant cependant. @Brian a suggéré de créer simplement un autre DictReader. Cela ne fonctionnera pas si votre premier lecteur est à mi-chemin de la lecture du fichier, car votre nouveau lecteur aura des clés et des valeurs inattendues où que vous soyez dans le fichier.
la source
Le protocole d'itérateur de Python est très simple et ne fournit qu'une seule méthode (
.next()
ou__next__()
), et aucune méthode pour réinitialiser un itérateur en général.Le modèle courant consiste à créer à nouveau un nouvel itérateur en utilisant la même procédure.
Si vous souhaitez "enregistrer" un itérateur afin de pouvoir revenir à son début, vous pouvez également bifurquer l'itérateur en utilisant
itertools.tee
la source
csv
module. Espérons que les deux réponses seront utiles à l'affiche originale.__iter__
. Autrement dit, les itérateurs doivent également être itérables.Oui , si vous utilisez
numpy.nditer
pour construire votre itérateur.la source
nditer
parcourir le tableau commeitertools.cycle
?try:
lenext()
et sur uneStopIteration
exception fairereset()
.next()
Il y a un bogue dans l'utilisation
.seek(0)
comme préconisé par Alex Martelli et Wilduck ci-dessus, à savoir que le prochain appel à.next()
vous donnera un dictionnaire de votre ligne d'en-tête sous la forme de{key1:key1, key2:key2, ...}
. Le travail autour consiste à suivrefile.seek(0)
avec un appel àreader.next()
pour se débarrasser de la ligne d'en-tête.Donc, votre code ressemblerait à ceci:
la source
C'est peut-être orthogonal à la question d'origine, mais on pourrait envelopper l'itérateur dans une fonction qui retourne l'itérateur.
Pour réinitialiser l'itérateur, il suffit de rappeler la fonction. Ceci est bien sûr trivial si la fonction lorsque ladite fonction ne prend aucun argument.
Dans le cas où la fonction nécessite des arguments, utilisez functools.partial pour créer une fermeture qui peut être passée à la place de l'itérateur d'origine.
Cela semble éviter la mise en cache que le tee (n copies) ou la liste (1 copie) aurait besoin de faire
la source
Pour les petits fichiers, vous pouvez envisager d'utiliser
more_itertools.seekable
- un outil tiers qui propose des itérables de réinitialisation.Démo
Production
Ici, a
DictReader
est enveloppé dans unseekable
objet (1) et avancé (2). Laseek()
méthode est utilisée pour réinitialiser / rembobiner l'itérateur à la position 0 (3).Remarque: la consommation de mémoire augmente avec l'itération, alors méfiez-vous en appliquant cet outil à des fichiers volumineux, comme indiqué dans la documentation .
la source
Bien qu'il n'y ait pas de réinitialisation d'itérateur, le module "itertools" de python 2.6 (et plus tard) a quelques utilitaires qui peuvent y aider. L'un d'eux est le «tee» qui peut faire plusieurs copies d'un itérateur, et mettre en cache les résultats de celui qui avance, de sorte que ces résultats soient utilisés sur les copies. Je vais répondre à vos objectifs:
la source
Pour DictReader:
Pour DictWriter:
la source
list(generator())
renvoie toutes les valeurs restantes pour un générateur et le réinitialise effectivement s'il n'est pas en boucle.la source
Problème
J'ai déjà eu le même problème. Après avoir analysé mon code, j'ai réalisé que tenter de réinitialiser l'itérateur à l'intérieur des boucles augmentait légèrement la complexité temporelle et rendait également le code un peu moche.
Solution
Ouvrez le fichier et enregistrez les lignes dans une variable en mémoire.
Vous pouvez désormais parcourir les lignes n'importe où dans votre portée sans avoir à utiliser d'itérateur.
la source
Une option possible est d'utiliser
itertools.cycle()
, ce qui vous permettra d'itérer indéfiniment sans aucune astuce comme.seek(0)
.la source
J'arrive à ce même problème - bien que j'aime la
tee()
solution, je ne sais pas quelle sera la taille de mes fichiers et les avertissements de mémoire concernant la consommation de l'un avant l'autre me découragent d'adopter cette méthode.Au lieu de cela, je crée une paire d'itérateurs en utilisant des
iter()
instructions, et en utilisant le premier pour mon passage initial, avant de passer au second pour l'exécution finale.Donc, dans le cas d'un lecteur de dict, si le lecteur est défini en utilisant:
Je peux créer une paire d'itérateurs à partir de cette "spécification" - en utilisant:
Je peux ensuite exécuter mon code de 1ère passe
d1
, en sachant que le deuxième itérateurd2
a été défini à partir de la même spécification racine.Je n'ai pas testé cela de manière exhaustive, mais cela semble fonctionner avec des données factices.
la source
Seulement si le type sous-jacent fournit un mécanisme pour le faire (par exemple
fp.seek(0)
).la source
Renvoie un itérateur nouvellement créé à la dernière itération lors de l'appel 'iter ()'
Production:
la source