Saya memiliki objek generator yang dikembalikan oleh banyak hasil. Persiapan untuk memanggil generator ini agak memakan waktu operasi. Itu sebabnya saya ingin menggunakan kembali generator beberapa kali.
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
Tentu saja, saya mempertimbangkan menyalin konten ke dalam daftar sederhana. Apakah ada cara untuk mereset generator saya?
y = list(y)
dengan sisa kode Anda tidak berubah.Generator tidak dapat diputar ulang. Anda memiliki opsi berikut:
Jalankan kembali fungsi generator, mulai ulang generasi:
Menyimpan hasil generator dalam struktur data pada memori atau disk yang dapat Anda ulangi lagi:
Kelemahan dari opsi 1 adalah bahwa ia menghitung nilai lagi. Jika CPU-intensif Anda akhirnya menghitung dua kali. Di sisi lain, kekurangan 2 adalah penyimpanan. Seluruh daftar nilai akan disimpan di memori. Jika ada terlalu banyak nilai, itu bisa tidak taktis.
Jadi, Anda memiliki memori klasik vs pemrosesan pengorbanan . Saya tidak bisa membayangkan cara memutar generator tanpa menyimpan nilai-nilai atau menghitungnya lagi.
sumber
sumber
Mungkin solusi paling sederhana adalah dengan membungkus bagian yang mahal dalam suatu objek dan meneruskannya ke generator:
Dengan cara ini, Anda dapat men-cache perhitungan yang mahal.
Jika Anda dapat menyimpan semua hasil dalam RAM secara bersamaan, gunakan
list()
untuk mematerialisasikan hasil generator dalam daftar sederhana dan bekerja dengannya.sumber
Saya ingin menawarkan solusi berbeda untuk masalah lama
Manfaat ini jika dibandingkan dengan sesuatu seperti
list(iterator)
ini adalahO(1)
kompleksitas ruang dan apalist(iterator)
adanyaO(n)
. Kerugiannya adalah, jika Anda hanya memiliki akses ke iterator, tetapi bukan fungsi yang menghasilkan iterator, maka Anda tidak dapat menggunakan metode ini. Misalnya, mungkin masuk akal untuk melakukan hal berikut, tetapi tidak akan berhasil.sumber
Jika jawaban GrzegorzOledzki tidak cukup, Anda mungkin dapat menggunakan
send()
untuk mencapai tujuan Anda. Lihat PEP-0342 untuk detail lebih lanjut tentang generator yang ditingkatkan dan ekspresi hasil.UPDATE: Lihat juga
itertools.tee()
. Ini melibatkan beberapa memori yang dibandingkan dengan pemrosesan tradeoff yang disebutkan di atas, tetapi mungkin menghemat beberapa memori lebih dari sekadar menyimpan hasil generator dalamlist
; itu tergantung pada bagaimana Anda menggunakan generator.sumber
Jika generator Anda murni dalam arti bahwa outputnya hanya bergantung pada argumen yang diteruskan dan nomor langkah, dan Anda ingin generator yang dihasilkan dapat di-restart, berikut ini cuplikan potongan yang mungkin berguna:
output:
sumber
Dari dokumentasi resmi tee :
Jadi yang terbaik adalah menggunakan
list(iterable)
dalam kasus Anda.sumber
list()
menempatkan seluruh iterable ke dalam memoritee()
jika satu iterator mengkonsumsi semua nilai - begitulah caratee
kerjanya.Menggunakan fungsi pembungkus untuk menangani
StopIteration
Anda bisa menulis fungsi pembungkus sederhana untuk fungsi generator yang melacak ketika generator habis. Ini akan melakukannya dengan menggunakan
StopIteration
pengecualian yang dilempar generator saat mencapai akhir iterasi.Seperti yang Anda lihat di atas, ketika fungsi wrapper kami menangkap
StopIteration
pengecualian, ia hanya menginisialisasi ulang objek generator (menggunakan instance lain dari panggilan fungsi).Dan kemudian, dengan asumsi Anda mendefinisikan fungsi penyediaan generator di suatu tempat seperti di bawah ini, Anda bisa menggunakan sintaks dekorator fungsi Python untuk membungkusnya secara implisit:
sumber
Anda dapat mendefinisikan fungsi yang mengembalikan generator Anda
Sekarang Anda bisa melakukan sebanyak yang Anda suka:
sumber
Saya tidak yakin apa yang Anda maksud dengan persiapan mahal, tapi saya kira Anda benar-benar memilikinya
Jika itu masalahnya, mengapa tidak digunakan kembali
data
?sumber
Tidak ada opsi untuk mengatur ulang iterator. Iterator biasanya muncul ketika iterate melalui
next()
fungsi. Satu-satunya cara adalah mengambil cadangan sebelum beralih pada objek iterator. Periksa di bawah.Membuat objek iterator dengan item 0 hingga 9
Iterasi melalui fungsi next () yang akan muncul
Konversi objek iterator ke daftar
jadi item 0 sudah muncul. Juga semua item muncul saat kami mengonversi iterator ke daftar.
Jadi, Anda perlu mengonversi iterator ke daftar untuk cadangan sebelum mulai mengulangi. Daftar dapat dikonversi ke iterator dengan
iter(<list-object>)
sumber
Anda sekarang dapat menggunakan
more_itertools.seekable
(alat pihak ketiga) yang memungkinkan pengaturan ulang iterator.Instal via
> pip install more_itertools
Catatan: konsumsi memori tumbuh saat memajukan iterator, jadi berhati-hatilah dengan iterables besar.
sumber
Anda bisa melakukannya dengan menggunakan itertools.cycle () Anda bisa membuat iterator dengan metode ini dan kemudian menjalankan for for over the iterator yang akan mengulang nilainya.
Sebagai contoh:
akan menghasilkan 20 angka, 0 hingga 4 berulang kali.
Catatan dari dokumen:
sumber
Ok, Anda bilang ingin memanggil generator beberapa kali, tetapi inisialisasi mahal ... Bagaimana dengan yang seperti ini?
Atau, Anda bisa membuat kelas Anda sendiri yang mengikuti protokol iterator dan mendefinisikan semacam fungsi 'reset'.
https://docs.python.org/2/library/stdtypes.html#iterator-types http://anandology.com/python-practice-book/iterators.html
sumber
__call__
Jawaban saya memecahkan masalah yang sedikit berbeda: Jika generator mahal untuk diinisialisasi dan setiap objek yang dihasilkan mahal untuk dihasilkan. Tetapi kita perlu mengkonsumsi generator beberapa kali dalam berbagai fungsi. Untuk memanggil generator dan setiap objek yang dihasilkan tepat sekali kita dapat menggunakan utas dan Jalankan masing-masing metode pengkonsumsi dalam utas yang berbeda. Kami mungkin tidak mencapai paralelisme sejati karena GIL, tetapi kami akan mencapai tujuan kami.
Pendekatan ini melakukan pekerjaan yang baik dalam kasus berikut: model pembelajaran yang mendalam memproses banyak gambar. Hasilnya banyak topeng untuk banyak objek pada gambar. Setiap topeng mengkonsumsi memori. Kami memiliki sekitar 10 metode yang membuat statistik dan metrik berbeda, tetapi mereka mengambil semua gambar sekaligus. Semua gambar tidak dapat ditampung dalam memori. Metode-metode tersebut dapat dengan mudah ditulis ulang untuk menerima iterator.
Penggunaan:
sumber
itertools.islice
atau untuk asyncaiostream.stream.take
, dan postingan ini memungkinkan Anda melakukannya dalam asyn / menunggu cara stackoverflow.com/a/42379188/149818Itu bisa dilakukan oleh objek kode. Inilah contohnya.
1 2 3 4
1 2 3 4
sumber
exec
sedikit yang tidak direkomendasikan untuk kasus sederhana tersebut.