Vejo muitas respostas sugerindo itertools.tee , mas isso é ignorar um aviso crucial nos documentos para isso:
Essa ferramenta pode exigir armazenamento auxiliar significativo (dependendo da quantidade de dados temporários que precisam ser armazenados). Em geral, se um iterador usa a maioria ou todos os dados antes de mais iteradoras começa, é mais rápido para usar list()em vez de tee().
Basicamente, ele teeé projetado para situações em que dois (ou mais) clones de um iterador, embora "saindo da sincronia" um com o outro, não o fazem muito - eles dizem na mesma "vizinhança" (um alguns itens atrás ou à frente um do outro). Não é adequado para o problema do OP de "refazer desde o início".
L = list(DictReader(...))por outro lado, é perfeitamente adequado, desde que a lista de ditados possa caber confortavelmente na memória. Um novo "iterador desde o início" (muito leve e de baixo custo) pode ser criado a qualquer momento iter(L)e usado em parte ou no todo, sem afetar os novos ou os existentes; outros padrões de acesso também estão facilmente disponíveis.
Como várias respostas comentaram corretamente, no caso específico de csvvocê também é possível .seek(0)o objeto de arquivo subjacente (um caso bastante especial). Não sei se isso está documentado e garantido, embora atualmente funcione; provavelmente valeria a pena considerar apenas os arquivos csv realmente enormes, nos quais listrecomendo que a abordagem geral tenha uma área de armazenamento de memória muito grande.
Então, você poderá obter a próxima linha com reader.next(), que deve gerar
{'a':1,'b':2,'c':3,'d':4}
usá-lo novamente produzirá
{'a':2,'b':3,'c':4,'d':5}
No entanto, neste momento, se você usar blah.seek(0), na próxima vez que ligar, reader.next()você receberá
{'a':1,'b':2,'c':3,'d':4}
novamente.
Essa parece ser a funcionalidade que você está procurando. Tenho certeza de que existem alguns truques associados a essa abordagem, dos quais não estou ciente. @ Brian sugeriu simplesmente criar outro DictReader. Isso não funcionará se você for o primeiro leitor a meio da leitura do arquivo, pois seu novo leitor terá chaves e valores inesperados em qualquer lugar do arquivo.
Foi o que minha teoria me disse, bom ver que o que eu pensei que deveria acontecer, acontece.
Wayne Werner
@Wilduck: o comportamento que você está descrevendo com outra instância do DictReader não acontecerá se você criar um novo arquivo e passar isso para o segundo DictReader, certo?
Se você tiver dois manipuladores de arquivos, eles se comportarão independentemente, sim.
Wilduck
24
Não. O protocolo do iterador do Python é muito simples e fornece apenas um único método ( .next()ou __next__()) e nenhum método para redefinir um iterador em geral.
O padrão comum é criar um novo iterador usando o mesmo procedimento novamente.
Se você deseja "salvar" um iterador para poder voltar ao início, também pode bifurcar o iterador usando itertools.tee
Enquanto sua análise do método .next () provavelmente está correta, existe uma maneira bastante simples de obter o que a operação está pedindo.
Wilduck 16/07/10
2
@Wilduck: Eu vejo que a sua resposta. Acabei de responder à pergunta do iterador e não faço ideia do csvmódulo. Espero que ambas as respostas sejam úteis para o pôster original.
U0b34a0f6ae 16/07
Estritamente, o protocolo do iterador também exige __iter__. Ou seja, os iteradores também precisam ser iteráveis.
Steve Jessop
11
Sim , se você usar numpy.nditerpara criar seu iterador.
Existe um erro no uso, .seek(0)conforme defendido por Alex Martelli e Wilduck acima, a saber, a próxima chamada para .next()fornecer um dicionário da linha do cabeçalho na forma de {key1:key1, key2:key2, ...}. A solução é seguir file.seek(0)com uma chamada reader.next()para se livrar da linha do cabeçalho.
Portanto, seu código seria algo como isto:
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)
Aqui a DictReaderé envolvido em um seekableobjeto (1) e avançado (2). O seek()método é usado para redefinir / rebobinar o iterador para a 0a posição (3).
Nota: o consumo de memória aumenta com a iteração; portanto, tenha cuidado ao aplicar esta ferramenta a arquivos grandes, conforme indicado nos documentos .
Embora não haja redefinição do iterador, o módulo "itertools" do python 2.6 (e posterior) possui alguns utilitários que podem ajudá-lo. Um deles é o "tee", que pode fazer várias cópias de um iterador e armazenar em cache os resultados do que está sendo executado adiante, para que esses resultados sejam usados nas cópias. Vou cumprir seus propósitos:
>>>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]
Eu já tive o mesmo problema antes. Depois de analisar meu código, percebi que tentar redefinir o iterador dentro de loops aumenta um pouco a complexidade do tempo e também torna o código um pouco feio.
Solução
Abra o arquivo e salve as linhas em uma variável na memória.
# 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)
Agora você pode percorrer as linhas em qualquer lugar do seu escopo sem precisar lidar com um iterador.
Estou chegando ao mesmo problema - embora goste da tee()solução, não sei qual será o tamanho dos meus arquivos e os avisos de memória sobre o consumo de um primeiro antes do outro me impedem de adotar esse método.
Em vez disso, estou criando um par de iteradores usando iter()instruções e usando o primeiro para o meu detalhamento inicial, antes de mudar para o segundo para o final.
Portanto, no caso de um dict-reader, se o leitor for definido usando:
d = csv.DictReader(f, delimiter=",")
Eu posso criar um par de iteradores a partir desta "especificação" - usando:
d1, d2 = iter(d), iter(d)
Posso então executar meu código de 1ª passagem d1, seguro, sabendo que o segundo iterador d2foi definido a partir da mesma especificação raiz.
Eu não testei isso exaustivamente, mas parece funcionar com dados fictícios.
Respostas:
Vejo muitas respostas sugerindo itertools.tee , mas isso é ignorar um aviso crucial nos documentos para isso:
Basicamente, ele
tee
é projetado para situações em que dois (ou mais) clones de um iterador, embora "saindo da sincronia" um com o outro, não o fazem muito - eles dizem na mesma "vizinhança" (um alguns itens atrás ou à frente um do outro). Não é adequado para o problema do OP de "refazer desde o início".L = list(DictReader(...))
por outro lado, é perfeitamente adequado, desde que a lista de ditados possa caber confortavelmente na memória. Um novo "iterador desde o início" (muito leve e de baixo custo) pode ser criado a qualquer momentoiter(L)
e usado em parte ou no todo, sem afetar os novos ou os existentes; outros padrões de acesso também estão facilmente disponíveis.Como várias respostas comentaram corretamente, no caso específico de
csv
você também é possível.seek(0)
o objeto de arquivo subjacente (um caso bastante especial). Não sei se isso está documentado e garantido, embora atualmente funcione; provavelmente valeria a pena considerar apenas os arquivos csv realmente enormes, nos quaislist
recomendo que a abordagem geral tenha uma área de armazenamento de memória muito grande.fonte
list()
cache de várias páginas em um arquivo csvreader em um arquivo de 5 MB faz com que meu tempo de execução vá de ~ 12s para ~ 0,5s.Se você tiver um arquivo csv chamado 'blah.csv', será semelhante a
você sabe que pode abrir o arquivo para leitura e criar um DictReader com
Então, você poderá obter a próxima linha com
reader.next()
, que deve gerarusá-lo novamente produzirá
No entanto, neste momento, se você usar
blah.seek(0)
, na próxima vez que ligar,reader.next()
você receberánovamente.
Essa parece ser a funcionalidade que você está procurando. Tenho certeza de que existem alguns truques associados a essa abordagem, dos quais não estou ciente. @ Brian sugeriu simplesmente criar outro DictReader. Isso não funcionará se você for o primeiro leitor a meio da leitura do arquivo, pois seu novo leitor terá chaves e valores inesperados em qualquer lugar do arquivo.
fonte
Não. O protocolo do iterador do Python é muito simples e fornece apenas um único método (
.next()
ou__next__()
) e nenhum método para redefinir um iterador em geral.O padrão comum é criar um novo iterador usando o mesmo procedimento novamente.
Se você deseja "salvar" um iterador para poder voltar ao início, também pode bifurcar o iterador usando
itertools.tee
fonte
csv
módulo. Espero que ambas as respostas sejam úteis para o pôster original.__iter__
. Ou seja, os iteradores também precisam ser iteráveis.Sim , se você usar
numpy.nditer
para criar seu iterador.fonte
nditer
percorrer a matriz comoitertools.cycle
?try:
onext()
e em umaStopIteration
exceção fazerreset()
.next()
Existe um erro no uso,
.seek(0)
conforme defendido por Alex Martelli e Wilduck acima, a saber, a próxima chamada para.next()
fornecer um dicionário da linha do cabeçalho na forma de{key1:key1, key2:key2, ...}
. A solução é seguirfile.seek(0)
com uma chamadareader.next()
para se livrar da linha do cabeçalho.Portanto, seu código seria algo como isto:
fonte
Talvez isso seja ortogonal à pergunta original, mas é possível agrupar o iterador em uma função que retorna o iterador.
Para redefinir o iterador, basta chamar a função novamente. É claro que isso é trivial se a função quando a referida função não aceita argumentos.
Caso a função exija alguns argumentos, use functools.partial para criar um fechamento que possa ser passado em vez do iterador original.
Isso parece evitar o armazenamento em cache que tee (n cópias) ou lista (1 cópia) precisariam fazer
fonte
Para arquivos pequenos, considere usar
more_itertools.seekable
- uma ferramenta de terceiros que oferece a possibilidade de redefinir iteráveis.Demo
Resultado
Aqui a
DictReader
é envolvido em umseekable
objeto (1) e avançado (2). Oseek()
método é usado para redefinir / rebobinar o iterador para a 0a posição (3).Nota: o consumo de memória aumenta com a iteração; portanto, tenha cuidado ao aplicar esta ferramenta a arquivos grandes, conforme indicado nos documentos .
fonte
Embora não haja redefinição do iterador, o módulo "itertools" do python 2.6 (e posterior) possui alguns utilitários que podem ajudá-lo. Um deles é o "tee", que pode fazer várias cópias de um iterador e armazenar em cache os resultados do que está sendo executado adiante, para que esses resultados sejam usados nas cópias. Vou cumprir seus propósitos:
fonte
Para DictReader:
Para DictWriter:
fonte
list(generator())
retorna todos os valores restantes para um gerador e o redefine efetivamente se não estiver em loop.fonte
Problema
Eu já tive o mesmo problema antes. Depois de analisar meu código, percebi que tentar redefinir o iterador dentro de loops aumenta um pouco a complexidade do tempo e também torna o código um pouco feio.
Solução
Abra o arquivo e salve as linhas em uma variável na memória.
Agora você pode percorrer as linhas em qualquer lugar do seu escopo sem precisar lidar com um iterador.
fonte
Uma opção possível é usar
itertools.cycle()
, o que permitirá iterar indefinidamente, sem nenhum truque.seek(0)
.fonte
Estou chegando ao mesmo problema - embora goste da
tee()
solução, não sei qual será o tamanho dos meus arquivos e os avisos de memória sobre o consumo de um primeiro antes do outro me impedem de adotar esse método.Em vez disso, estou criando um par de iteradores usando
iter()
instruções e usando o primeiro para o meu detalhamento inicial, antes de mudar para o segundo para o final.Portanto, no caso de um dict-reader, se o leitor for definido usando:
Eu posso criar um par de iteradores a partir desta "especificação" - usando:
Posso então executar meu código de 1ª passagem
d1
, seguro, sabendo que o segundo iteradord2
foi definido a partir da mesma especificação raiz.Eu não testei isso exaustivamente, mas parece funcionar com dados fictícios.
fonte
Somente se o tipo subjacente fornecer um mecanismo para fazer isso (por exemplo
fp.seek(0)
).fonte
Retornar um iterador recém-criado na última iteração durante a chamada 'iter ()'
Resultado:
fonte