Untuk apa Anda menggunakan fungsi generator Python?
213
Saya mulai belajar Python dan saya menemukan fungsi generator, yang memiliki pernyataan hasil di dalamnya. Saya ingin tahu jenis masalah apa yang fungsi-fungsi ini benar-benar pecahkan.
Generator memberi Anda evaluasi malas. Anda menggunakannya dengan mengulanginya, baik secara eksplisit dengan 'untuk' atau secara implisit dengan meneruskannya ke fungsi apa pun atau membangun yang diulanginya. Anda dapat menganggap generator sebagai mengembalikan beberapa item, seolah-olah mereka mengembalikan daftar, tetapi alih-alih mengembalikannya sekaligus, mereka mengembalikannya satu per satu, dan fungsi generator dijeda hingga item berikutnya diminta.
Generator bagus untuk menghitung set besar hasil (khususnya perhitungan yang melibatkan loop sendiri) di mana Anda tidak tahu apakah Anda akan membutuhkan semua hasil, atau di mana Anda tidak ingin mengalokasikan memori untuk semua hasil pada saat yang sama . Atau untuk situasi di mana generator menggunakan generator lain , atau mengkonsumsi sumber daya lain, dan itu lebih nyaman jika itu terjadi selambat mungkin.
Penggunaan lain untuk generator (yang benar-benar sama) adalah untuk mengganti panggilan balik dengan iterasi. Dalam beberapa situasi Anda ingin fungsi melakukan banyak pekerjaan dan sesekali melaporkan kembali ke pemanggil. Secara tradisional Anda akan menggunakan fungsi panggilan balik untuk ini. Anda meneruskan panggilan balik ini ke fungsi-kerja dan secara berkala akan memanggil panggilan balik ini. Pendekatan generator adalah bahwa fungsi-kerja (sekarang generator) tidak tahu apa-apa tentang panggilan balik, dan hanya menghasilkan kapan pun ia ingin melaporkan sesuatu. Penelepon, alih-alih menulis callback terpisah dan meneruskannya ke fungsi-fungsi, melakukan semua pelaporan dengan sedikit putaran 'untuk' di sekitar generator.
Misalnya, Anda menulis program 'pencarian sistem file'. Anda dapat melakukan pencarian secara keseluruhan, mengumpulkan hasil dan kemudian menampilkannya satu per satu. Semua hasil harus dikumpulkan sebelum Anda menunjukkan yang pertama, dan semua hasil akan di memori pada saat yang sama. Atau Anda bisa menampilkan hasilnya saat Anda menemukannya, yang akan lebih hemat memori dan lebih ramah terhadap pengguna. Yang terakhir dapat dilakukan dengan melewatkan fungsi pencetakan hasil ke fungsi pencarian sistem file, atau bisa dilakukan dengan hanya membuat fungsi pencarian generator dan mengulangi hasilnya.
Jika Anda ingin melihat contoh dari dua pendekatan terakhir, lihat os.path.walk () (fungsi berjalan sistem file lama dengan callback) dan os.walk () (generator berjalan sistem file baru.) Tentu saja, jika Anda benar-benar ingin mengumpulkan semua hasil dalam daftar, pendekatan generator sepele untuk dikonversi ke pendekatan daftar besar:
Apakah generator seperti yang menghasilkan daftar filesystem melakukan tindakan secara paralel dengan kode yang menjalankan generator itu dalam satu lingkaran? Idealnya komputer akan menjalankan body of the loop (memproses hasil terakhir) sambil melakukan apa pun yang harus dilakukan generator untuk mendapatkan nilai berikutnya.
Steven Lu
@ StevenLu: Kecuali jika ada masalah untuk meluncurkan utas secara manual sebelum yielddan joinsesudahnya untuk mendapatkan hasil selanjutnya, ia tidak mengeksekusi secara paralel (dan tidak ada generator perpustakaan standar yang melakukan ini; diam-diam meluncurkan utas tidak disukai). Generator berhenti di masing-masing yieldhingga nilai berikutnya diminta. Jika generator membungkus I / O, OS mungkin secara proaktif menyimpan data dari file dengan asumsi itu akan diminta segera, tapi itu OS, Python tidak terlibat.
ShadowRanger
90
Salah satu alasan untuk menggunakan generator adalah untuk membuat solusi lebih jelas untuk beberapa jenis solusi.
Yang lain adalah memperlakukan hasil satu per satu, menghindari membuat daftar hasil yang sangat besar yang akan Anda proses pisahkan.
Jika Anda memiliki fungsi fibonacci-up-to-n seperti ini:
# function versiondef fibon(n):
a = b =1
result =[]for i in xrange(n):
result.append(a)
a, b = b, a + b
return result
Anda dapat lebih mudah menulis fungsi karena ini:
# generator versiondef fibon(n):
a = b =1for i in xrange(n):yield a
a, b = b, a + b
Fungsinya lebih jelas. Dan jika Anda menggunakan fungsi seperti ini:
for x in fibon(1000000):print x,
dalam contoh ini, jika menggunakan versi generator, seluruh daftar item 1000000 tidak akan dibuat sama sekali, hanya satu nilai pada suatu waktu. Itu tidak akan menjadi kasus ketika menggunakan versi daftar, di mana daftar akan dibuat terlebih dahulu.
Penggunaan generator yang tidak jelas adalah menciptakan fungsi yang dapat terputus, yang memungkinkan Anda melakukan hal-hal seperti memperbarui UI atau menjalankan beberapa pekerjaan "secara bersamaan" (disisipkan, sebenarnya) tanpa menggunakan utas.
Bagian Motivasi bagus karena memiliki contoh spesifik: "Ketika fungsi produsen memiliki pekerjaan yang cukup sulit sehingga memerlukan mempertahankan keadaan di antara nilai-nilai yang dihasilkan, sebagian besar bahasa pemrograman tidak menawarkan solusi yang menyenangkan dan efisien selain menambahkan fungsi panggilan balik ke argumen produsen. daftar ... Misalnya, tokenize.py di perpustakaan standar mengambil pendekatan ini "
Ben Creasy
38
Saya menemukan penjelasan ini yang menghilangkan keraguan saya. Karena ada kemungkinan orang yang tidak tahu Generatorsjuga tidak tahuyield
Kembali
Pernyataan pengembalian adalah tempat semua variabel lokal dihancurkan dan nilai yang dihasilkan dikembalikan (dikembalikan) ke pemanggil. Jika fungsi yang sama dipanggil beberapa waktu kemudian, fungsi tersebut akan mendapatkan serangkaian variabel baru.
Menghasilkan
Tetapi bagaimana jika variabel lokal tidak dibuang ketika kita keluar dari suatu fungsi? Ini menyiratkan bahwa kita bisa di resume the functionmana kita tinggalkan. Di sinilah konsep generatorsdiperkenalkan dan yieldpernyataan dilanjutkan di mana yang functionditinggalkan.
def generate_integers(N):for i in xrange(N):yield i
In[1]: gen = generate_integers(3)In[2]: gen
<generator object at 0x8117f90>In[3]: gen.next()0In[4]: gen.next()1In[5]: gen.next()
Jadi itulah perbedaan antara returndan yieldpernyataan dalam Python.
Pernyataan hasil adalah apa yang membuat fungsi menjadi fungsi generator.
Jadi generator adalah alat sederhana dan kuat untuk membuat iterator. Mereka ditulis seperti fungsi biasa, tetapi mereka menggunakan yieldpernyataan kapan pun mereka ingin mengembalikan data. Setiap kali next () dipanggil, generator melanjutkan di tempat yang ditinggalkannya (ia mengingat semua nilai data dan pernyataan mana yang terakhir dieksekusi).
Katakanlah Anda memiliki 100 juta domain di tabel MySQL Anda, dan Anda ingin memperbarui peringkat Alexa untuk setiap domain.
Hal pertama yang Anda butuhkan adalah memilih nama domain Anda dari database.
Katakanlah nama tabel Anda adalah domainsdan nama kolom adalah domain.
Jika Anda menggunakannya SELECT domain FROM domainsakan mengembalikan 100 juta baris yang akan menghabiskan banyak memori. Jadi server Anda mungkin macet.
Jadi Anda memutuskan untuk menjalankan program dalam batch. Katakanlah ukuran batch kami adalah 1000.
Dalam batch pertama kami, kami akan meminta 1000 baris pertama, memeriksa peringkat Alexa untuk setiap domain dan memperbarui baris database.
Dalam batch kedua kami, kami akan bekerja pada 1000 baris berikutnya. Dalam batch ketiga kami akan dari 2001 hingga 3000 dan seterusnya.
Sekarang kita membutuhkan fungsi generator yang menghasilkan batch kita.
Inilah fungsi generator kami:
defResultGenerator(cursor, batchsize=1000):whileTrue:
results = cursor.fetchmany(batchsize)ifnot results:breakfor result in results:yield result
Seperti yang Anda lihat, fungsi kami menyimpan yieldhasilnya. Jika Anda menggunakan kata kunci returnalih-alih yield, maka seluruh fungsi akan berakhir setelah mencapai kembali.
return- returns only once
yield- returns multiple times
Jika suatu fungsi menggunakan kata kunci yieldmaka itu adalah generator.
Sekarang Anda dapat mengulangi seperti ini:
db =MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")for result inResultGenerator(cursor):
doSomethingWith(result)
db.close()
itu akan lebih praktis, jika hasil dapat dijelaskan dalam hal pemrograman rekursif / dyanmic!
igaurav
27
Buffering. Ketika efisien untuk mengambil data dalam potongan besar, tetapi memprosesnya dalam potongan kecil, maka generator mungkin membantu:
def bufferedFetch():whileTrue:
buffer = getBigChunkOfData()# insert some code to break on 'end of data'for i in buffer:yield i
Di atas memungkinkan Anda dengan mudah memisahkan buffering dari pemrosesan. Fungsi konsumen sekarang bisa mendapatkan nilai satu per satu tanpa khawatir tentang buffering.
Jika getBigChuckOfData tidak malas, maka saya tidak mengerti apa manfaat yang didapat di sini. Apa itu use case untuk fungsi ini?
Sean Geoffrey Pietz
1
Tapi intinya adalah bahwa, IIUC, bufferedFetch adalah pemanggilan panggilan untuk getBigChunkOfData. Jika getBigChunkOfData sudah malas, maka bufferedFetch akan sia-sia. Setiap panggilan ke bufferedFetch () akan mengembalikan satu elemen buffer, meskipun BigChunk sudah dibaca. Dan Anda tidak perlu secara eksplisit menyimpan hitungan elemen berikutnya untuk kembali, karena mekanisme hasil melakukan hal itu secara implisit.
hmijail meratapi orang-orang yang mengundurkan diri
21
Saya telah menemukan bahwa generator sangat membantu dalam membersihkan kode Anda dan dengan memberi Anda cara yang sangat unik untuk merangkum dan memodulasi kode. Dalam situasi di mana Anda perlu sesuatu untuk terus memuntahkan nilai-nilai berdasarkan proses internal sendiri dan ketika bahwa kebutuhan sesuatu yang disebut dari mana saja di kode Anda (dan bukan hanya dalam loop atau blok misalnya), generator yang fitur untuk menggunakan.
Contoh abstrak akan menjadi penghasil angka Fibonacci yang tidak hidup dalam satu lingkaran dan ketika dipanggil dari mana saja akan selalu mengembalikan angka berikutnya dalam urutan:
def fib():
first =0
second =1yield first
yield second
while1:
next = first + second
yield next
first = second
second = next
fibgen1 = fib()
fibgen2 = fib()
Sekarang Anda memiliki dua objek penghasil angka Fibonacci yang dapat Anda panggil dari mana saja dalam kode Anda dan mereka akan selalu mengembalikan angka Fibonacci yang lebih besar secara berurutan sebagai berikut:
Hal yang indah tentang generator adalah bahwa mereka merangkum keadaan tanpa harus melalui lingkaran menciptakan objek. Salah satu cara berpikir tentang mereka adalah sebagai "fungsi" yang mengingat keadaan internal mereka.
Saya mendapatkan contoh Fibonacci dari Python Generator - Apa itu? dan dengan sedikit imajinasi, Anda dapat menemukan banyak situasi lain di mana generator membuat alternatif yang bagus untuk forloop dan konstruksi iterasi tradisional lainnya.
Penjelasan sederhana: Pertimbangkan sebuah forpernyataan
for item in iterable:
do_stuff()
Banyak waktu, semua item di iterabletidak perlu ada di sana sejak awal, tetapi dapat dihasilkan dengan cepat sesuai kebutuhan. Ini bisa menjadi jauh lebih efisien di keduanya
space (Anda tidak perlu menyimpan semua item secara bersamaan) dan
waktu (iterasi dapat selesai sebelum semua item diperlukan).
Di lain waktu, Anda bahkan tidak tahu semua item sebelumnya. Sebagai contoh:
for command in user_input():
do_stuff_with(command)
Anda tidak memiliki cara untuk mengetahui semua perintah pengguna sebelumnya, tetapi Anda dapat menggunakan loop yang bagus seperti ini jika Anda memiliki generator yang memberi Anda perintah:
... dan urutan yang tak terbatas dapat dihasilkan dengan berulang-ulang memutar daftar kecil, kembali ke awal setelah akhir tercapai. Saya menggunakan ini untuk memilih warna dalam grafik, atau menghasilkan throbbers atau pemintal yang sibuk dalam teks.
Penggunaan favorit saya adalah "filter" dan "kurangi" operasi.
Katakanlah kita sedang membaca file, dan hanya ingin baris yang dimulai dengan "##".
def filter2sharps( aSequence ):for l in aSequence:if l.startswith("##"):yield l
Kita kemudian dapat menggunakan fungsi generator dalam loop yang tepat
source= file(...)for line in filter2sharps( source.readlines()):print line
source.close()
Contoh pengurangannya serupa. Katakanlah kita memiliki file di mana kita perlu menemukan blok <Location>...</Location>garis. [Bukan tag HTML, tapi garis yang terlihat seperti tag.]
def reduceLocation( aSequence ):
keep=False
block=Nonefor line in aSequence:if line.startswith("</Location"):
block.append( line )yield block
block=None
keep=Falseelif line.startsWith("<Location"):
block=[ line ]
keep=Trueelif keep:
block.append( line )else:passif block isnotNone:yield block # A partial block, icky
Sekali lagi, kita bisa menggunakan generator ini untuk loop yang tepat.
source = file(...)for b in reduceLocation( source.readlines()):print b
source.close()
Idenya adalah bahwa fungsi generator memungkinkan kita untuk menyaring atau mengurangi urutan, menghasilkan urutan lain satu nilai pada satu waktu.
fileobj.readlines()akan membaca seluruh file ke daftar di memori, mengalahkan tujuan menggunakan generator. Karena objek file sudah dapat diubah, Anda dapat menggunakannya for b in your_generator(fileobject):. Dengan begitu file Anda akan dibaca satu baris pada satu waktu, untuk menghindari membaca seluruh file.
nosklo
MengurangiLokasi cukup aneh menghasilkan daftar, mengapa tidak hanya menghasilkan setiap baris? Juga filter dan kurangi adalah builtin dengan perilaku yang diharapkan (lihat bantuan di ipython dll.), Penggunaan "pengurangan" Anda sama dengan filter.
James Antill
Poin bagus di readlines (). Saya biasanya menyadari bahwa file adalah iterator garis kelas satu selama pengujian unit.
S.Lott
Sebenarnya, "reduksi" menggabungkan beberapa garis individu menjadi objek komposit. Oke, ini daftar, tapi itu masih pengurangan yang diambil dari sumbernya.
S.Lott
9
Contoh praktis di mana Anda dapat menggunakan generator adalah jika Anda memiliki semacam bentuk dan Anda ingin beralih di sudut, tepi atau apa pun. Untuk proyek saya sendiri (kode sumber di sini ) saya memiliki sebuah persegi panjang:
Sekarang saya bisa membuat persegi panjang dan loop di sudut-sudutnya:
myrect=Rect(50,50,100,100)for corner in myrect:print(corner)
Alih-alih __iter__Anda bisa memiliki metode iter_cornersdan menyebutnya dengan for corner in myrect.iter_corners(). Hanya saja lebih elegan untuk digunakan __iter__karena kita bisa menggunakan nama instance kelas secara langsung dalam forekspresi.
Namun, beberapa jawaban yang bagus di sini, saya juga merekomendasikan pembacaan lengkap tutorial Pemrograman Fungsional Python yang membantu menjelaskan beberapa kasus penggunaan generator yang lebih kuat.
Karena metode pengiriman generator belum disebutkan, berikut adalah contohnya:
def test():for i in xrange(5):
val =yieldprint(val)
t = test()# Proceed to 'yield' statement
next(t)# Send value to yield
t.send(1)
t.send('2')
t.send([3])
Ini menunjukkan kemungkinan untuk mengirim nilai ke generator yang sedang berjalan. Kursus yang lebih maju tentang generator dalam video di bawah ini (termasukyield dari eksplorasi, generator untuk pemrosesan paralel, lolos dari batas rekursi, dll.)
Tumpukan barang. Kapan saja Anda ingin membuat urutan item, tetapi tidak ingin harus 'mematerialisasikan' semuanya menjadi daftar sekaligus. Misalnya, Anda dapat memiliki generator sederhana yang mengembalikan bilangan prima:
def primes():
primes_found = set()
primes_found.add(2)yield2for i in itertools.count(1):
candidate = i *2+1ifnot all(candidate % prime for prime in primes_found):
primes_found.add(candidate)yield candidate
Anda kemudian dapat menggunakannya untuk menghasilkan produk dari bilangan prima berikutnya:
def prime_products():
primeiter = primes()
prev = primeiter.next()for prime in primeiter:yield prime * prev
prev = prime
Ini adalah contoh yang cukup sepele, tetapi Anda dapat melihat bagaimana hal itu berguna untuk memproses dataset besar (berpotensi tak terbatas!) Tanpa membuatnya terlebih dahulu, yang hanya salah satu kegunaan yang lebih jelas.
jika tidak ada (kandidat% prime untuk prime di primes_found) seharusnya jika semua (kandidat% prime untuk prime di primes_found)
rjmunro
Ya, saya bermaksud menulis "jika tidak ada (kandidat% prime == 0 untuk prime di primes_found). Namun, Anda sedikit lebih rapi. :)
Nick Johnson
Saya kira Anda lupa menghapus 'tidak' dari jika tidak semua (kandidat% prime untuk prime di primes_found)
Thava
0
Juga bagus untuk mencetak bilangan prima hingga n:
def genprime(n=10):for num in range(3, n+1):for factor in range(2, num):if num%factor ==0:breakelse:yield(num)for prime_num in genprime(100):print(prime_num)
Jawaban:
Generator memberi Anda evaluasi malas. Anda menggunakannya dengan mengulanginya, baik secara eksplisit dengan 'untuk' atau secara implisit dengan meneruskannya ke fungsi apa pun atau membangun yang diulanginya. Anda dapat menganggap generator sebagai mengembalikan beberapa item, seolah-olah mereka mengembalikan daftar, tetapi alih-alih mengembalikannya sekaligus, mereka mengembalikannya satu per satu, dan fungsi generator dijeda hingga item berikutnya diminta.
Generator bagus untuk menghitung set besar hasil (khususnya perhitungan yang melibatkan loop sendiri) di mana Anda tidak tahu apakah Anda akan membutuhkan semua hasil, atau di mana Anda tidak ingin mengalokasikan memori untuk semua hasil pada saat yang sama . Atau untuk situasi di mana generator menggunakan generator lain , atau mengkonsumsi sumber daya lain, dan itu lebih nyaman jika itu terjadi selambat mungkin.
Penggunaan lain untuk generator (yang benar-benar sama) adalah untuk mengganti panggilan balik dengan iterasi. Dalam beberapa situasi Anda ingin fungsi melakukan banyak pekerjaan dan sesekali melaporkan kembali ke pemanggil. Secara tradisional Anda akan menggunakan fungsi panggilan balik untuk ini. Anda meneruskan panggilan balik ini ke fungsi-kerja dan secara berkala akan memanggil panggilan balik ini. Pendekatan generator adalah bahwa fungsi-kerja (sekarang generator) tidak tahu apa-apa tentang panggilan balik, dan hanya menghasilkan kapan pun ia ingin melaporkan sesuatu. Penelepon, alih-alih menulis callback terpisah dan meneruskannya ke fungsi-fungsi, melakukan semua pelaporan dengan sedikit putaran 'untuk' di sekitar generator.
Misalnya, Anda menulis program 'pencarian sistem file'. Anda dapat melakukan pencarian secara keseluruhan, mengumpulkan hasil dan kemudian menampilkannya satu per satu. Semua hasil harus dikumpulkan sebelum Anda menunjukkan yang pertama, dan semua hasil akan di memori pada saat yang sama. Atau Anda bisa menampilkan hasilnya saat Anda menemukannya, yang akan lebih hemat memori dan lebih ramah terhadap pengguna. Yang terakhir dapat dilakukan dengan melewatkan fungsi pencetakan hasil ke fungsi pencarian sistem file, atau bisa dilakukan dengan hanya membuat fungsi pencarian generator dan mengulangi hasilnya.
Jika Anda ingin melihat contoh dari dua pendekatan terakhir, lihat os.path.walk () (fungsi berjalan sistem file lama dengan callback) dan os.walk () (generator berjalan sistem file baru.) Tentu saja, jika Anda benar-benar ingin mengumpulkan semua hasil dalam daftar, pendekatan generator sepele untuk dikonversi ke pendekatan daftar besar:
sumber
yield
danjoin
sesudahnya untuk mendapatkan hasil selanjutnya, ia tidak mengeksekusi secara paralel (dan tidak ada generator perpustakaan standar yang melakukan ini; diam-diam meluncurkan utas tidak disukai). Generator berhenti di masing-masingyield
hingga nilai berikutnya diminta. Jika generator membungkus I / O, OS mungkin secara proaktif menyimpan data dari file dengan asumsi itu akan diminta segera, tapi itu OS, Python tidak terlibat.Salah satu alasan untuk menggunakan generator adalah untuk membuat solusi lebih jelas untuk beberapa jenis solusi.
Yang lain adalah memperlakukan hasil satu per satu, menghindari membuat daftar hasil yang sangat besar yang akan Anda proses pisahkan.
Jika Anda memiliki fungsi fibonacci-up-to-n seperti ini:
Anda dapat lebih mudah menulis fungsi karena ini:
Fungsinya lebih jelas. Dan jika Anda menggunakan fungsi seperti ini:
dalam contoh ini, jika menggunakan versi generator, seluruh daftar item 1000000 tidak akan dibuat sama sekali, hanya satu nilai pada suatu waktu. Itu tidak akan menjadi kasus ketika menggunakan versi daftar, di mana daftar akan dibuat terlebih dahulu.
sumber
list(fibon(5))
Lihat bagian "Motivasi" di PEP 255 .
Penggunaan generator yang tidak jelas adalah menciptakan fungsi yang dapat terputus, yang memungkinkan Anda melakukan hal-hal seperti memperbarui UI atau menjalankan beberapa pekerjaan "secara bersamaan" (disisipkan, sebenarnya) tanpa menggunakan utas.
sumber
Saya menemukan penjelasan ini yang menghilangkan keraguan saya. Karena ada kemungkinan orang yang tidak tahu
Generators
juga tidak tahuyield
Kembali
Pernyataan pengembalian adalah tempat semua variabel lokal dihancurkan dan nilai yang dihasilkan dikembalikan (dikembalikan) ke pemanggil. Jika fungsi yang sama dipanggil beberapa waktu kemudian, fungsi tersebut akan mendapatkan serangkaian variabel baru.
Menghasilkan
Tetapi bagaimana jika variabel lokal tidak dibuang ketika kita keluar dari suatu fungsi? Ini menyiratkan bahwa kita bisa di
resume the function
mana kita tinggalkan. Di sinilah konsepgenerators
diperkenalkan danyield
pernyataan dilanjutkan di mana yangfunction
ditinggalkan.Jadi itulah perbedaan antara
return
danyield
pernyataan dalam Python.Pernyataan hasil adalah apa yang membuat fungsi menjadi fungsi generator.
Jadi generator adalah alat sederhana dan kuat untuk membuat iterator. Mereka ditulis seperti fungsi biasa, tetapi mereka menggunakan
yield
pernyataan kapan pun mereka ingin mengembalikan data. Setiap kali next () dipanggil, generator melanjutkan di tempat yang ditinggalkannya (ia mengingat semua nilai data dan pernyataan mana yang terakhir dieksekusi).sumber
Contoh Dunia Nyata
Katakanlah Anda memiliki 100 juta domain di tabel MySQL Anda, dan Anda ingin memperbarui peringkat Alexa untuk setiap domain.
Hal pertama yang Anda butuhkan adalah memilih nama domain Anda dari database.
Katakanlah nama tabel Anda adalah
domains
dan nama kolom adalahdomain
.Jika Anda menggunakannya
SELECT domain FROM domains
akan mengembalikan 100 juta baris yang akan menghabiskan banyak memori. Jadi server Anda mungkin macet.Jadi Anda memutuskan untuk menjalankan program dalam batch. Katakanlah ukuran batch kami adalah 1000.
Dalam batch pertama kami, kami akan meminta 1000 baris pertama, memeriksa peringkat Alexa untuk setiap domain dan memperbarui baris database.
Dalam batch kedua kami, kami akan bekerja pada 1000 baris berikutnya. Dalam batch ketiga kami akan dari 2001 hingga 3000 dan seterusnya.
Sekarang kita membutuhkan fungsi generator yang menghasilkan batch kita.
Inilah fungsi generator kami:
Seperti yang Anda lihat, fungsi kami menyimpan
yield
hasilnya. Jika Anda menggunakan kata kuncireturn
alih-alihyield
, maka seluruh fungsi akan berakhir setelah mencapai kembali.Jika suatu fungsi menggunakan kata kunci
yield
maka itu adalah generator.Sekarang Anda dapat mengulangi seperti ini:
sumber
Buffering. Ketika efisien untuk mengambil data dalam potongan besar, tetapi memprosesnya dalam potongan kecil, maka generator mungkin membantu:
Di atas memungkinkan Anda dengan mudah memisahkan buffering dari pemrosesan. Fungsi konsumen sekarang bisa mendapatkan nilai satu per satu tanpa khawatir tentang buffering.
sumber
Saya telah menemukan bahwa generator sangat membantu dalam membersihkan kode Anda dan dengan memberi Anda cara yang sangat unik untuk merangkum dan memodulasi kode. Dalam situasi di mana Anda perlu sesuatu untuk terus memuntahkan nilai-nilai berdasarkan proses internal sendiri dan ketika bahwa kebutuhan sesuatu yang disebut dari mana saja di kode Anda (dan bukan hanya dalam loop atau blok misalnya), generator yang fitur untuk menggunakan.
Contoh abstrak akan menjadi penghasil angka Fibonacci yang tidak hidup dalam satu lingkaran dan ketika dipanggil dari mana saja akan selalu mengembalikan angka berikutnya dalam urutan:
Sekarang Anda memiliki dua objek penghasil angka Fibonacci yang dapat Anda panggil dari mana saja dalam kode Anda dan mereka akan selalu mengembalikan angka Fibonacci yang lebih besar secara berurutan sebagai berikut:
Hal yang indah tentang generator adalah bahwa mereka merangkum keadaan tanpa harus melalui lingkaran menciptakan objek. Salah satu cara berpikir tentang mereka adalah sebagai "fungsi" yang mengingat keadaan internal mereka.
Saya mendapatkan contoh Fibonacci dari Python Generator - Apa itu? dan dengan sedikit imajinasi, Anda dapat menemukan banyak situasi lain di mana generator membuat alternatif yang bagus untuk
for
loop dan konstruksi iterasi tradisional lainnya.sumber
Penjelasan sederhana: Pertimbangkan sebuah
for
pernyataanBanyak waktu, semua item di
iterable
tidak perlu ada di sana sejak awal, tetapi dapat dihasilkan dengan cepat sesuai kebutuhan. Ini bisa menjadi jauh lebih efisien di keduanyaDi lain waktu, Anda bahkan tidak tahu semua item sebelumnya. Sebagai contoh:
Anda tidak memiliki cara untuk mengetahui semua perintah pengguna sebelumnya, tetapi Anda dapat menggunakan loop yang bagus seperti ini jika Anda memiliki generator yang memberi Anda perintah:
Dengan generator Anda juga dapat memiliki iterasi lebih dari urutan yang tak terbatas, yang tentu saja tidak mungkin ketika iterasi di atas kontainer.
sumber
itertool
untuk itu - lihatcycles
.Penggunaan favorit saya adalah "filter" dan "kurangi" operasi.
Katakanlah kita sedang membaca file, dan hanya ingin baris yang dimulai dengan "##".
Kita kemudian dapat menggunakan fungsi generator dalam loop yang tepat
Contoh pengurangannya serupa. Katakanlah kita memiliki file di mana kita perlu menemukan blok
<Location>...</Location>
garis. [Bukan tag HTML, tapi garis yang terlihat seperti tag.]Sekali lagi, kita bisa menggunakan generator ini untuk loop yang tepat.
Idenya adalah bahwa fungsi generator memungkinkan kita untuk menyaring atau mengurangi urutan, menghasilkan urutan lain satu nilai pada satu waktu.
sumber
fileobj.readlines()
akan membaca seluruh file ke daftar di memori, mengalahkan tujuan menggunakan generator. Karena objek file sudah dapat diubah, Anda dapat menggunakannyafor b in your_generator(fileobject):
. Dengan begitu file Anda akan dibaca satu baris pada satu waktu, untuk menghindari membaca seluruh file.Contoh praktis di mana Anda dapat menggunakan generator adalah jika Anda memiliki semacam bentuk dan Anda ingin beralih di sudut, tepi atau apa pun. Untuk proyek saya sendiri (kode sumber di sini ) saya memiliki sebuah persegi panjang:
Sekarang saya bisa membuat persegi panjang dan loop di sudut-sudutnya:
Alih-alih
__iter__
Anda bisa memiliki metodeiter_corners
dan menyebutnya denganfor corner in myrect.iter_corners()
. Hanya saja lebih elegan untuk digunakan__iter__
karena kita bisa menggunakan nama instance kelas secara langsung dalamfor
ekspresi.sumber
Pada dasarnya menghindari fungsi-fungsi panggilan balik ketika iterating atas input mempertahankan status.
Lihat di sini dan di sini untuk ikhtisar tentang apa yang dapat dilakukan dengan menggunakan generator.
sumber
Namun, beberapa jawaban yang bagus di sini, saya juga merekomendasikan pembacaan lengkap tutorial Pemrograman Fungsional Python yang membantu menjelaskan beberapa kasus penggunaan generator yang lebih kuat.
sumber
Karena metode pengiriman generator belum disebutkan, berikut adalah contohnya:
Ini menunjukkan kemungkinan untuk mengirim nilai ke generator yang sedang berjalan. Kursus yang lebih maju tentang generator dalam video di bawah ini (termasuk
yield
dari eksplorasi, generator untuk pemrosesan paralel, lolos dari batas rekursi, dll.)David Beazley menggunakan generator di PyCon 2014
sumber
Saya menggunakan generator ketika server web kami bertindak sebagai proxy:
sumber
Tumpukan barang. Kapan saja Anda ingin membuat urutan item, tetapi tidak ingin harus 'mematerialisasikan' semuanya menjadi daftar sekaligus. Misalnya, Anda dapat memiliki generator sederhana yang mengembalikan bilangan prima:
Anda kemudian dapat menggunakannya untuk menghasilkan produk dari bilangan prima berikutnya:
Ini adalah contoh yang cukup sepele, tetapi Anda dapat melihat bagaimana hal itu berguna untuk memproses dataset besar (berpotensi tak terbatas!) Tanpa membuatnya terlebih dahulu, yang hanya salah satu kegunaan yang lebih jelas.
sumber
Juga bagus untuk mencetak bilangan prima hingga n:
sumber