Di samping catatan, saya menemukan bahwa list()fungsi akan mengulangi argumennya (an iterable). Jadi memanggil list()pada iterable yang sama dua kali (misalnya hasil dari zip()) Anda akan mendapatkan daftar kosong pada panggilan kedua!
theaws.blog
Jawaban:
84
Saya melihat banyak jawaban yang menyarankan itertools.tee , tetapi itu mengabaikan satu peringatan penting di dokumen untuk itu:
Alat itert ini mungkin memerlukan penyimpanan tambahan yang signifikan (tergantung pada seberapa banyak data sementara perlu disimpan). Secara umum, jika satu iterator menggunakan sebagian besar atau semua data sebelum iterator lain dimulai, itu lebih cepat digunakan list()daripada tee().
Pada dasarnya, teedirancang untuk situasi di mana dua (atau lebih) klon dari satu iterator, sementara "keluar dari sinkronisasi" satu sama lain, tidak melakukannya dengan banyak - sebaliknya, mereka mengatakan di "sekitar" yang sama (a beberapa item di belakang atau di depan satu sama lain). Tidak cocok untuk masalah OP "ulangi dari awal".
L = list(DictReader(...))di sisi lain sangat cocok, selama daftar dicts bisa masuk dengan nyaman dalam memori. Sebuah "iterator dari awal" (sangat ringan dan overhead rendah) dapat dibuat kapan saja dengan iter(L), dan digunakan sebagian atau seluruhnya tanpa mempengaruhi yang baru atau yang sudah ada; pola akses lainnya juga tersedia dengan mudah.
Seperti yang dikatakan beberapa jawaban dengan benar, dalam kasus tertentu csvAnda juga dapat .seek(0)objek file yang mendasarinya (kasus yang agak khusus). Saya tidak yakin itu didokumentasikan dan dijamin, meskipun saat ini berfungsi; itu mungkin akan layak dipertimbangkan hanya untuk file csv yang benar-benar besar, di mana listsaya merekomendasikan sebagai pendekatan umum akan memiliki jejak memori yang terlalu besar.
Kemudian, Anda akan bisa mendapatkan baris berikutnya dengan reader.next(), yang seharusnya menghasilkan
{'a':1,'b':2,'c':3,'d':4}
menggunakannya lagi akan menghasilkan
{'a':2,'b':3,'c':4,'d':5}
Namun, pada titik ini jika Anda menggunakan blah.seek(0), lain kali Anda menelepon reader.next()Anda akan mendapatkan
{'a':1,'b':2,'c':3,'d':4}
lagi.
Tampaknya ini adalah fungsi yang Anda cari. Saya yakin ada beberapa trik yang terkait dengan pendekatan ini yang tidak saya sadari. @ Brian menyarankan untuk membuat DictReader lain. Ini tidak akan berfungsi jika Anda adalah pembaca pertama setengah jalan membaca file, karena pembaca baru Anda akan memiliki kunci dan nilai yang tidak terduga dari mana pun Anda berada dalam file.
Inilah yang dikatakan teori saya, senang melihat apa yang saya pikir harus terjadi, ternyata.
Wayne Werner
@Wilduck: perilaku yang Anda gambarkan dengan contoh lain DictReader tidak akan terjadi jika Anda membuat pegangan file baru dan meneruskannya ke DictReader kedua, bukan?
Jika Anda memiliki dua penangan file, mereka akan berperilaku secara independen, ya.
Wilduck
24
Tidak. Protokol iterator Python sangat sederhana, dan hanya menyediakan satu metode ( .next()atau __next__()), dan tidak ada metode untuk mereset iterator secara umum.
Pola yang umum adalah membuat iterator baru menggunakan prosedur yang sama lagi.
Jika Anda ingin "menyimpan" sebuah iterator sehingga Anda dapat kembali ke awal, Anda juga dapat melakukan fork iterator dengan menggunakan itertools.tee
Meskipun analisis Anda tentang metode .next () mungkin benar, ada cara yang cukup sederhana untuk mendapatkan apa yang diminta oleh operasi.
Wilduck
2
@ Wilduck: Saya melihat bahwa jawaban Anda. Saya baru saja menjawab pertanyaan iterator, dan saya tidak tahu tentang csvmodulnya. Semoga kedua jawaban tersebut bermanfaat untuk poster aslinya.
u0b34a0f6ae
Secara ketat, protokol iterator juga membutuhkan __iter__. Artinya, iterator juga diharuskan untuk menjadi iterable.
Steve Jessop
11
Iya , jika Anda menggunakan numpy.nditeruntuk membangun iterator Anda.
Ada bug dalam penggunaan .seek(0)seperti yang dianjurkan oleh Alex Martelli dan Wilduck di atas, yaitu panggilan berikutnya ke .next()akan memberikan kamus baris header Anda dalam bentuk {key1:key1, key2:key2, ...}. Solusinya adalah mengikuti file.seek(0)dengan panggilan untuk reader.next()menyingkirkan baris header.
Jadi kode Anda akan terlihat seperti ini:
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)
Di sini a DictReaderdibungkus dengan seekableobjek (1) dan lanjutan (2). Ituseek() metode yang digunakan untuk me-reset / mundur iterator ke posisi 0 (3).
Catatan: konsumsi memori bertambah dengan iterasi, jadi berhati-hatilah dalam menerapkan alat ini ke file besar, seperti yang ditunjukkan dalam dokumen .
Meskipun tidak ada reset iterator, modul "itertools" dari python 2.6 (dan yang lebih baru) memiliki beberapa utilitas yang dapat membantu di sana. Salah satunya adalah "tee" yang dapat membuat banyak salinan dari sebuah iterator, dan menyimpan hasil dari iterator yang berjalan di depan, sehingga hasil ini digunakan pada salinan. Saya akan melihat tujuan Anda:
>>>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]
Saya pernah mengalami masalah yang sama sebelumnya. Setelah menganalisis kode saya, saya menyadari bahwa mencoba mengatur ulang iterator di dalam loop sedikit meningkatkan kompleksitas waktu dan itu juga membuat kode sedikit jelek.
Larutan
Buka file dan simpan baris ke variabel dalam memori.
# 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)
Sekarang Anda dapat melakukan loop melalui baris di mana saja dalam lingkup Anda tanpa berurusan dengan iterator.
Salah satu opsi yang memungkinkan adalah menggunakan itertools.cycle(), yang memungkinkan Anda mengulang tanpa batas waktu tanpa trik seperti itu .seek(0).
Saya sampai pada masalah yang sama ini - sementara saya menyukai tee()solusinya, saya tidak tahu seberapa besar file saya nantinya dan peringatan memori tentang mengkonsumsi satu terlebih dahulu sebelum yang lain membuat saya tidak bisa mengadopsi metode itu.
Sebagai gantinya, saya membuat sepasang iterator menggunakan iter()pernyataan, dan menggunakan yang pertama untuk proses awal saya, sebelum beralih ke yang kedua untuk proses terakhir.
Jadi, dalam kasus dict-reader, jika reader didefinisikan menggunakan:
d = csv.DictReader(f, delimiter=",")
Saya dapat membuat sepasang iterator dari "spesifikasi" ini - menggunakan:
d1, d2 = iter(d), iter(d)
Saya kemudian dapat menjalankan kode akses pertama saya d1, aman karena mengetahui bahwa iterator kedua d2telah ditentukan dari spesifikasi root yang sama.
Saya belum menguji ini secara menyeluruh, tetapi tampaknya berfungsi dengan data tiruan.
list()
fungsi akan mengulangi argumennya (an iterable). Jadi memanggillist()
pada iterable yang sama dua kali (misalnya hasil darizip()
) Anda akan mendapatkan daftar kosong pada panggilan kedua!Jawaban:
Saya melihat banyak jawaban yang menyarankan itertools.tee , tetapi itu mengabaikan satu peringatan penting di dokumen untuk itu:
Pada dasarnya,
tee
dirancang untuk situasi di mana dua (atau lebih) klon dari satu iterator, sementara "keluar dari sinkronisasi" satu sama lain, tidak melakukannya dengan banyak - sebaliknya, mereka mengatakan di "sekitar" yang sama (a beberapa item di belakang atau di depan satu sama lain). Tidak cocok untuk masalah OP "ulangi dari awal".L = list(DictReader(...))
di sisi lain sangat cocok, selama daftar dicts bisa masuk dengan nyaman dalam memori. Sebuah "iterator dari awal" (sangat ringan dan overhead rendah) dapat dibuat kapan saja denganiter(L)
, dan digunakan sebagian atau seluruhnya tanpa mempengaruhi yang baru atau yang sudah ada; pola akses lainnya juga tersedia dengan mudah.Seperti yang dikatakan beberapa jawaban dengan benar, dalam kasus tertentu
csv
Anda juga dapat.seek(0)
objek file yang mendasarinya (kasus yang agak khusus). Saya tidak yakin itu didokumentasikan dan dijamin, meskipun saat ini berfungsi; itu mungkin akan layak dipertimbangkan hanya untuk file csv yang benar-benar besar, di manalist
saya merekomendasikan sebagai pendekatan umum akan memiliki jejak memori yang terlalu besar.sumber
list()
untuk cache multipassage melalui csvreader pada file 5MB melihat runtime saya pergi dari ~ 12secs ke ~ 0.5s.Jika Anda memiliki file csv bernama 'blah.csv' Sepertinya
Anda tahu bahwa Anda dapat membuka file untuk membaca, dan membuat DictReader dengan
Kemudian, Anda akan bisa mendapatkan baris berikutnya dengan
reader.next()
, yang seharusnya menghasilkanmenggunakannya lagi akan menghasilkan
Namun, pada titik ini jika Anda menggunakan
blah.seek(0)
, lain kali Anda meneleponreader.next()
Anda akan mendapatkanlagi.
Tampaknya ini adalah fungsi yang Anda cari. Saya yakin ada beberapa trik yang terkait dengan pendekatan ini yang tidak saya sadari. @ Brian menyarankan untuk membuat DictReader lain. Ini tidak akan berfungsi jika Anda adalah pembaca pertama setengah jalan membaca file, karena pembaca baru Anda akan memiliki kunci dan nilai yang tidak terduga dari mana pun Anda berada dalam file.
sumber
Tidak. Protokol iterator Python sangat sederhana, dan hanya menyediakan satu metode (
.next()
atau__next__()
), dan tidak ada metode untuk mereset iterator secara umum.Pola yang umum adalah membuat iterator baru menggunakan prosedur yang sama lagi.
Jika Anda ingin "menyimpan" sebuah iterator sehingga Anda dapat kembali ke awal, Anda juga dapat melakukan fork iterator dengan menggunakan
itertools.tee
sumber
csv
modulnya. Semoga kedua jawaban tersebut bermanfaat untuk poster aslinya.__iter__
. Artinya, iterator juga diharuskan untuk menjadi iterable.Iya , jika Anda menggunakan
numpy.nditer
untuk membangun iterator Anda.sumber
nditer
siklus melalui array sepertiitertools.cycle
?try:
dengannext()
dan padaStopIteration
pengecualian melakukanreset()
.next()
Ada bug dalam penggunaan
.seek(0)
seperti yang dianjurkan oleh Alex Martelli dan Wilduck di atas, yaitu panggilan berikutnya ke.next()
akan memberikan kamus baris header Anda dalam bentuk{key1:key1, key2:key2, ...}
. Solusinya adalah mengikutifile.seek(0)
dengan panggilan untukreader.next()
menyingkirkan baris header.Jadi kode Anda akan terlihat seperti ini:
sumber
Ini mungkin ortogonal dengan pertanyaan awal, tetapi iterator dapat digabungkan dalam fungsi yang mengembalikan iterator.
Untuk mengatur ulang iterator cukup panggil fungsinya lagi. Ini tentu saja sepele jika fungsi saat fungsi tersebut tidak membutuhkan argumen.
Jika fungsinya memerlukan beberapa argumen, gunakan functools.pihak untuk membuat penutupan yang bisa dilewatkan sebagai ganti iterator asli.
Ini tampaknya untuk menghindari caching yang perlu dilakukan oleh tee (n salinan) atau daftar (1 salinan)
sumber
Untuk file kecil, Anda dapat mempertimbangkan untuk menggunakan
more_itertools.seekable
- alat pihak ketiga yang menawarkan pengulangan ulang.Demo
Keluaran
Di sini a
DictReader
dibungkus denganseekable
objek (1) dan lanjutan (2). Ituseek()
metode yang digunakan untuk me-reset / mundur iterator ke posisi 0 (3).Catatan: konsumsi memori bertambah dengan iterasi, jadi berhati-hatilah dalam menerapkan alat ini ke file besar, seperti yang ditunjukkan dalam dokumen .
sumber
Meskipun tidak ada reset iterator, modul "itertools" dari python 2.6 (dan yang lebih baru) memiliki beberapa utilitas yang dapat membantu di sana. Salah satunya adalah "tee" yang dapat membuat banyak salinan dari sebuah iterator, dan menyimpan hasil dari iterator yang berjalan di depan, sehingga hasil ini digunakan pada salinan. Saya akan melihat tujuan Anda:
sumber
Untuk DictReader:
Untuk DictWriter:
sumber
list(generator())
mengembalikan semua nilai yang tersisa untuk generator dan secara efektif menyetel ulang jika tidak diulang.sumber
Masalah
Saya pernah mengalami masalah yang sama sebelumnya. Setelah menganalisis kode saya, saya menyadari bahwa mencoba mengatur ulang iterator di dalam loop sedikit meningkatkan kompleksitas waktu dan itu juga membuat kode sedikit jelek.
Larutan
Buka file dan simpan baris ke variabel dalam memori.
Sekarang Anda dapat melakukan loop melalui baris di mana saja dalam lingkup Anda tanpa berurusan dengan iterator.
sumber
Salah satu opsi yang memungkinkan adalah menggunakan
itertools.cycle()
, yang memungkinkan Anda mengulang tanpa batas waktu tanpa trik seperti itu.seek(0)
.sumber
Saya sampai pada masalah yang sama ini - sementara saya menyukai
tee()
solusinya, saya tidak tahu seberapa besar file saya nantinya dan peringatan memori tentang mengkonsumsi satu terlebih dahulu sebelum yang lain membuat saya tidak bisa mengadopsi metode itu.Sebagai gantinya, saya membuat sepasang iterator menggunakan
iter()
pernyataan, dan menggunakan yang pertama untuk proses awal saya, sebelum beralih ke yang kedua untuk proses terakhir.Jadi, dalam kasus dict-reader, jika reader didefinisikan menggunakan:
Saya dapat membuat sepasang iterator dari "spesifikasi" ini - menggunakan:
Saya kemudian dapat menjalankan kode akses pertama saya
d1
, aman karena mengetahui bahwa iterator keduad2
telah ditentukan dari spesifikasi root yang sama.Saya belum menguji ini secara menyeluruh, tetapi tampaknya berfungsi dengan data tiruan.
sumber
Hanya jika tipe yang mendasari menyediakan mekanisme untuk melakukannya (misalnya
fp.seek(0)
).sumber
Kembalikan iterator yang baru dibuat pada iterasi terakhir selama panggilan 'iter ()'
Keluaran:
sumber