Veo muchas respuestas que sugieren itertools.tee , pero eso ignora una advertencia crucial en los documentos para ello:
Esta herramienta iterativa puede requerir un almacenamiento auxiliar significativo (dependiendo de la cantidad de datos temporales que se deban almacenar). En general, si un iterador usa la mayoría o la totalidad de los datos antes de que comience otro iterador, es más rápido usarlo en list()lugar de hacerlo tee().
Básicamente, teeestá diseñado para aquellas situaciones en las que dos (o más) clones de un iterador, mientras "se desincronizan" entre sí, no lo hacen por mucho , más bien, dicen en la misma "vecindad" (un pocos elementos uno detrás del otro). No es adecuado para el problema del OP de "rehacer desde el principio".
L = list(DictReader(...))Por otro lado, es perfectamente adecuado, siempre y cuando la lista de dictos pueda caber cómodamente en la memoria. Se puede crear un nuevo "iterador desde el principio" (muy ligero y de bajo costo) en cualquier momento coniter(L) , y se puede usar en parte o en su totalidad sin afectar a los nuevos o existentes; otros patrones de acceso también están fácilmente disponibles.
Como varias respuestas señalaron correctamente, en el caso específico de csvusted también puede .seek(0)el objeto de archivo subyacente (un caso bastante especial). No estoy seguro de que esté documentado y garantizado, aunque actualmente funciona; probablemente valdría la pena considerarlo solo para archivos csv verdaderamente enormes, en los listque recomiendo que el enfoque general tenga una huella de memoria demasiado grande.
Luego, podrá obtener la siguiente línea con reader.next(), que debería generar
{'a':1,'b':2,'c':3,'d':4}
usarlo nuevamente producirá
{'a':2,'b':3,'c':4,'d':5}
Sin embargo, en este punto si usa blah.seek(0), la próxima vez que llame reader.next()obtendrá
{'a':1,'b':2,'c':3,'d':4}
de nuevo.
Esta parece ser la funcionalidad que estás buscando. Sin embargo, estoy seguro de que hay algunos trucos asociados con este enfoque que no conozco. @Brian sugirió simplemente crear otro DictReader. Esto no funcionará si su primer lector está a la mitad de la lectura del archivo, ya que su nuevo lector tendrá claves y valores inesperados desde cualquier lugar del archivo.
Esto fue lo que me dijo mi teoría, es bueno ver que lo que pensé que debería pasar, sí.
Wayne Werner
@Wilduck: el comportamiento que está describiendo con otra instancia de DictReader no sucederá si crea un nuevo identificador de archivo y lo pasa al segundo DictReader, ¿verdad?
Si tiene dos manejadores de archivos, se comportarán independientemente, sí.
Wilduck
24
No. El protocolo de iterador de Python es muy simple y solo proporciona un único método ( .next()o __next__()), y ningún método para restablecer un iterador en general.
El patrón común es, en cambio, crear un nuevo iterador utilizando el mismo procedimiento nuevamente.
Si desea "guardar" un iterador para poder volver a su inicio, también puede bifurcar el iterador utilizando itertools.tee
Si bien el análisis del método .next () es probablemente correcto, hay una forma bastante simple de obtener lo que está pidiendo el operador.
Wilduck
2
@Wilduck: veo que tu respuesta. Acabo de responder la pregunta del iterador, y no tengo idea sobre el csvmódulo. Esperemos que ambas respuestas sean útiles para el póster original.
u0b34a0f6ae
Estrictamente, el protocolo iterador también requiere __iter__. Es decir, los iteradores también deben ser iterables.
Steve Jessop
11
Sí , si usas numpy.nditerpara construir tu iterador.
Hay un error en el uso .seek(0)como lo recomiendan Alex Martelli y Wilduck anteriormente, a saber, que la próxima llamada a .next()le dará un diccionario de su fila de encabezado en forma de {key1:key1, key2:key2, ...}. La solución es seguir file.seek(0)con una llamada para reader.next()deshacerse de la fila del encabezado.
Entonces su código se vería así:
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)
Esto es quizás ortogonal a la pregunta original, pero uno podría ajustar el iterador en una función que devuelve el iterador.
def get_iter():return iterator
Para restablecer el iterador simplemente llame a la función nuevamente. Esto es, por supuesto, trivial si la función cuando dicha función no toma argumentos.
En el caso de que la función requiera algunos argumentos, use functools.partial para crear un cierre que se pueda pasar en lugar del iterador original.
Aquí a DictReaderestá envuelto en un seekableobjeto (1) y avanzado (2). losseek() método se utiliza para restablecer / rebobinar el iterador a la posición 0 (3).
Nota: el consumo de memoria aumenta con la iteración, así que tenga cuidado al aplicar esta herramienta a archivos grandes, como se indica en los documentos .
Si bien no hay restablecimiento de iterador, el módulo "itertools" de python 2.6 (y posterior) tiene algunas utilidades que pueden ayudarlo. Uno de ellos es el "tee", que puede hacer múltiples copias de un iterador y almacenar en caché los resultados del que está por delante, para que estos resultados se utilicen en las copias. Cortaré tus 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]
He tenido el mismo problema antes. Después de analizar mi código, me di cuenta de que intentar restablecer el iterador dentro de los bucles aumenta ligeramente la complejidad del tiempo y también hace que el código sea un poco feo.
Solución
Abra el archivo y guarde las filas en una variable en la memoria.
# 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)
Ahora puede recorrer las filas en cualquier lugar de su alcance sin tener que lidiar con un iterador.
Estoy llegando a este mismo problema, aunque me gusta el tee() solución, no sé qué tan grandes van a ser mis archivos y las advertencias de memoria acerca de consumir uno primero antes de que el otro me desanime a adoptar ese método.
En cambio, estoy creando un par de iteradores usando iter() declaraciones, y usando el primero para mi ejecución inicial, antes de cambiar al segundo para la ejecución final.
Entonces, en el caso de un dict-reader, si el lector se define usando:
d = csv.DictReader(f, delimiter=",")
Puedo crear un par de iteradores a partir de esta "especificación", utilizando:
d1, d2 = iter(d), iter(d)
Entonces puedo ejecutar mi código de primer paso contra d1, seguro sabiendo que el segundo iterador d2se ha definido a partir de la misma especificación raíz.
No lo he probado exhaustivamente, pero parece funcionar con datos ficticios.
Respuestas:
Veo muchas respuestas que sugieren itertools.tee , pero eso ignora una advertencia crucial en los documentos para ello:
Básicamente,
tee
está diseñado para aquellas situaciones en las que dos (o más) clones de un iterador, mientras "se desincronizan" entre sí, no lo hacen por mucho , más bien, dicen en la misma "vecindad" (un pocos elementos uno detrás del otro). No es adecuado para el problema del OP de "rehacer desde el principio".L = list(DictReader(...))
Por otro lado, es perfectamente adecuado, siempre y cuando la lista de dictos pueda caber cómodamente en la memoria. Se puede crear un nuevo "iterador desde el principio" (muy ligero y de bajo costo) en cualquier momento coniter(L)
, y se puede usar en parte o en su totalidad sin afectar a los nuevos o existentes; otros patrones de acceso también están fácilmente disponibles.Como varias respuestas señalaron correctamente, en el caso específico de
csv
usted también puede.seek(0)
el objeto de archivo subyacente (un caso bastante especial). No estoy seguro de que esté documentado y garantizado, aunque actualmente funciona; probablemente valdría la pena considerarlo solo para archivos csv verdaderamente enormes, en loslist
que recomiendo que el enfoque general tenga una huella de memoria demasiado grande.fuente
list()
de caché multipassage a través de un csvreader en un archivo de 5 MB hace que mi tiempo de ejecución pase de ~ 12 segundos a ~ 0.5 s.Si tiene un archivo csv llamado 'blah.csv', parece que
sabes que puedes abrir el archivo para leerlo y crear un DictReader con
Luego, podrá obtener la siguiente línea con
reader.next()
, que debería generarusarlo nuevamente producirá
Sin embargo, en este punto si usa
blah.seek(0)
, la próxima vez que llamereader.next()
obtendráde nuevo.
Esta parece ser la funcionalidad que estás buscando. Sin embargo, estoy seguro de que hay algunos trucos asociados con este enfoque que no conozco. @Brian sugirió simplemente crear otro DictReader. Esto no funcionará si su primer lector está a la mitad de la lectura del archivo, ya que su nuevo lector tendrá claves y valores inesperados desde cualquier lugar del archivo.
fuente
No. El protocolo de iterador de Python es muy simple y solo proporciona un único método (
.next()
o__next__()
), y ningún método para restablecer un iterador en general.El patrón común es, en cambio, crear un nuevo iterador utilizando el mismo procedimiento nuevamente.
Si desea "guardar" un iterador para poder volver a su inicio, también puede bifurcar el iterador utilizando
itertools.tee
fuente
csv
módulo. Esperemos que ambas respuestas sean útiles para el póster original.__iter__
. Es decir, los iteradores también deben ser iterables.Sí , si usas
numpy.nditer
para construir tu iterador.fuente
nditer
recorrer la matriz comoitertools.cycle
?try:
elnext()
y en unaStopIteration
excepción hacerreset()
.next()
Hay un error en el uso
.seek(0)
como lo recomiendan Alex Martelli y Wilduck anteriormente, a saber, que la próxima llamada a.next()
le dará un diccionario de su fila de encabezado en forma de{key1:key1, key2:key2, ...}
. La solución es seguirfile.seek(0)
con una llamada parareader.next()
deshacerse de la fila del encabezado.Entonces su código se vería así:
fuente
Esto es quizás ortogonal a la pregunta original, pero uno podría ajustar el iterador en una función que devuelve el iterador.
Para restablecer el iterador simplemente llame a la función nuevamente. Esto es, por supuesto, trivial si la función cuando dicha función no toma argumentos.
En el caso de que la función requiera algunos argumentos, use functools.partial para crear un cierre que se pueda pasar en lugar del iterador original.
Esto parece evitar el almacenamiento en caché que tendrían que hacer tee (n copias) o list (1 copia)
fuente
Para archivos pequeños, puede considerar usar
more_itertools.seekable
una herramienta de terceros que ofrece restablecer iterables.Manifestación
Salida
Aquí a
DictReader
está envuelto en unseekable
objeto (1) y avanzado (2). losseek()
método se utiliza para restablecer / rebobinar el iterador a la posición 0 (3).Nota: el consumo de memoria aumenta con la iteración, así que tenga cuidado al aplicar esta herramienta a archivos grandes, como se indica en los documentos .
fuente
Si bien no hay restablecimiento de iterador, el módulo "itertools" de python 2.6 (y posterior) tiene algunas utilidades que pueden ayudarlo. Uno de ellos es el "tee", que puede hacer múltiples copias de un iterador y almacenar en caché los resultados del que está por delante, para que estos resultados se utilicen en las copias. Cortaré tus propósitos:
fuente
Para DictReader:
Para DictWriter:
fuente
list(generator())
devuelve todos los valores restantes para un generador y lo restablece efectivamente si no está en bucle.fuente
Problema
He tenido el mismo problema antes. Después de analizar mi código, me di cuenta de que intentar restablecer el iterador dentro de los bucles aumenta ligeramente la complejidad del tiempo y también hace que el código sea un poco feo.
Solución
Abra el archivo y guarde las filas en una variable en la memoria.
Ahora puede recorrer las filas en cualquier lugar de su alcance sin tener que lidiar con un iterador.
fuente
Una opción posible es usarla
itertools.cycle()
, lo que te permitirá iterar indefinidamente sin ningún truco.seek(0)
.fuente
Estoy llegando a este mismo problema, aunque me gusta el
tee()
solución, no sé qué tan grandes van a ser mis archivos y las advertencias de memoria acerca de consumir uno primero antes de que el otro me desanime a adoptar ese método.En cambio, estoy creando un par de iteradores usando
iter()
declaraciones, y usando el primero para mi ejecución inicial, antes de cambiar al segundo para la ejecución final.Entonces, en el caso de un dict-reader, si el lector se define usando:
Puedo crear un par de iteradores a partir de esta "especificación", utilizando:
Entonces puedo ejecutar mi código de primer paso contra
d1
, seguro sabiendo que el segundo iteradord2
se ha definido a partir de la misma especificación raíz.No lo he probado exhaustivamente, pero parece funcionar con datos ficticios.
fuente
Solo si el tipo subyacente proporciona un mecanismo para hacerlo (por ejemplo
fp.seek(0)
).fuente
Devuelve un iterador recién creado en la última iteración durante la llamada 'iter ()'
Salida:
fuente