Last chance! 50% off DataCamp Premium
Sale ends in
Fungsi Python tidak selalu memiliki pernyataan return. Fungsi generator adalah fungsi yang memiliki kata kunci yield alih-alih return.
Fungsi-fungsi ini menghasilkan iterator generator, yaitu objek yang merepresentasikan aliran data. Elemen yang direpresentasikan oleh sebuah iterator dibuat dan dihasilkan hanya saat diperlukan. Jenis evaluasi ini sering disebut sebagai lazy evaluation.
Saat bekerja dengan dataset besar, generator menawarkan alternatif yang hemat memori dibanding menyimpan data dalam list, tuple, dan struktur data lain yang memerlukan ruang memori untuk setiap elemennya. Fungsi generator juga dapat membuat iterator tak hingga, yang tidak mungkin dilakukan dengan struktur yang dievaluasi secara eager seperti list dan tuple.
Sebelum mulai, mari rekap perbedaan antara fungsi dan generator:
|
Fitur |
Fungsi |
Generator |
|
Produksi Nilai |
Mengembalikan semua nilai sekaligus |
Menghasilkan nilai satu per satu, sesuai permintaan |
|
Eksekusi |
Dieksekusi sampai selesai sebelum mengembalikan hasil |
Berhenti sejenak setelah menghasilkan nilai, dilanjutkan saat nilai berikutnya diminta |
|
Kata Kunci |
return |
yield |
|
Penggunaan Memori |
Berpotensi tinggi, menyimpan seluruh deret di memori |
Rendah, hanya menyimpan nilai saat ini dan state untuk berikutnya |
|
Iterasi |
Dapat diiterasi berkali-kali, tetapi perlu menyimpan seluruh deret |
Dirancang untuk iterasi sekali lintas, lebih efisien untuk deret besar atau tak hingga |
yield di Python untuk Membuat Fungsi GeneratorIstilah generator di Python dapat merujuk pada iterator generator atau fungsi generator. Keduanya berbeda tetapi saling terkait di Python. Dalam tutorial ini, istilah lengkap sering digunakan untuk menghindari kebingungan.
Mari telusuri fungsi generator terlebih dahulu. Fungsi generator tampak mirip dengan fungsi biasa tetapi berisi kata kunci yield alih-alih return.
Saat sebuah program Python memanggil fungsi generator, ia membuat iterator generator. Iterator menghasilkan nilai sesuai permintaan dan menghentikan eksekusi sampai nilai lain diperlukan. Mari lihat contoh untuk menjelaskan konsep ini dan menunjukkan perbedaan antara fungsi biasa dan fungsi generator.
Pertama, mari mendefinisikan fungsi biasa yang berisi pernyataan return. Fungsi ini menerima deret kata dan sebuah huruf, lalu mengembalikan list yang berisi jumlah kemunculan huruf tersebut di setiap kata:
def find_letter_occurrences(words, letter):
output = []
for word in words:
output.append(word.count(letter))
return output
print(
find_letter_occurrences(["apple", "banana", "cherry"], "a")
)
[1, 3, 0]
Fungsi tersebut menghasilkan list berisi 1, 3, dan 0 karena ada satu a pada apple, tiga kemunculan a pada banana, dan tidak ada pada cherry. Fungsi yang sama dapat direfaktor untuk menggunakan list comprehension alih-alih menginisialisasi list kosong dan menggunakan .append():
def find_letter_occurrences(words, letter):
return [word.count(letter) for word in words]
Fungsi biasa ini mengembalikan list yang berisi semua hasil setiap kali dipanggil. Namun, jika list kata sangat besar, memanggil fungsi biasa ini membebani kebutuhan memori karena program membuat dan menyimpan list baru dengan ukuran yang sama seperti aslinya. Jika fungsi ini digunakan berulang pada beberapa argumen masukan, atau fungsi serupa melakukan operasi lain pada data asli, tekanan pada memori dapat meningkat dengan cepat.
Sebagai gantinya, kita dapat menggunakan fungsi generator:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
print(output)
<generator object find_letter_occurrences at 0x102935e00>
Fungsi tersebut menyertakan kata kunci yield keyword alih-alih return. Fungsi generator ini mengembalikan objek generator saat dipanggil, yang ditetapkan ke output. Objek ini adalah sebuah iterator. Objek ini tidak berisi data jumlah kemunculan huruf pada setiap kata. Sebaliknya, generator akan membuat dan menghasilkan nilai saat dibutuhkan. Mari ambil nilai pertama dari iterator generator ini:
print(next(output))
1
Fungsi bawaan next() adalah salah satu cara untuk mendapatkan nilai berikutnya dari sebuah iterator. Kita akan melihat cara lain nanti dalam tutorial ini.
Kode dalam fungsi generator dieksekusi hingga program mencapai baris dengan kata kunci yield. Pada contoh ini, loop for memulai iterasi pertama dan mengambil elemen pertama dalam list words. Metode string .count() mengembalikan bilangan bulat, yaitu 1 dalam kasus ini, karena ada satu kemunculan a pada apple. Generator menghasilkan nilai ini, yang dikembalikan oleh next(output).
Generator output berhenti sejenak eksekusinya pada titik ini. Jadi, generator menyelesaikan iterasi pertama dari loop for dan menemukan jumlah kemunculan huruf a pada kata pertama dalam list kata. Sekarang ia menunggu hingga dibutuhkan lagi.
Jika fungsi bawaan next() dipanggil lagi dengan argumen output, generator akan melanjutkan eksekusi dari titik saat ia berhenti:
print(next(output))
3
Generator melanjutkan dari baris dengan yield pada iterasi pertama loop for. Karena loop for tidak memiliki baris kode lebih lanjut, ia kembali ke atas loop dan mengambil elemen kedua dari list words. Nilai yang dikembalikan oleh .count() adalah 3 dalam kasus ini, dan nilai ini dihasilkan. Generator kembali berhenti sejenak pada titik eksekusi ini.
Panggilan ketiga ke next() melanjutkan eksekusi ini:
print(next(output))
0
Iterasi kedua mencapai akhir loop for, yang kemudian berlanjut ke iterasi ketiga. Kode bergerak ke baris dengan yield lagi, kali ini menghasilkan bilangan bulat 0 karena tidak ada kemunculan a pada cherry.
Generator kembali berhenti sejenak. Program hanya menentukan nasib generator saat kita memanggil next() untuk keempat kalinya:
print(next(output))
Traceback (most recent call last):
...
StopIteration
Eksekusi dilanjutkan dari akhir iterasi ketiga loop for. Namun, loop telah mencapai akhir iterasinya karena tidak ada lagi elemen dalam list words. Generator menimbulkan pengecualian StopIteration.
Dalam sebagian besar kasus penggunaan, elemen generator tidak diakses langsung menggunakan next() melainkan melalui proses iterasi lain. Pengecualian StopIteration menandakan akhir proses iterasi. Kita akan mengeksplorasi ini lebih lanjut pada bagian berikut dari tutorial ini.
Python memiliki cara lain untuk membuat iterator generator ketika operasinya dapat direpresentasikan oleh satu ekspresi, seperti pada contoh sebelumnya. Iterator generator output dapat dibuat menggunakan ekspresi generator:
words = ["apple", "banana", "cherry"]
letter = "a"
output = (word.count(letter) for word in words)
print(next(output))
print(next(output))
print(next(output))
print(next(output))
1
3
0
Traceback (most recent call last):
...
StopIteration
Ekspresi dalam tanda kurung yang ditetapkan ke output adalah ekspresi generator, yang membuat iterator generator serupa dengan yang dihasilkan oleh fungsi generator find_letter_occurrences().
Mari akhiri bagian ini dengan contoh lain fungsi generator untuk menyoroti bagaimana eksekusi berhenti dan dilanjutkan setiap kali sebuah elemen dibutuhkan:
def show_status():
print("Start")
yield
print("Middle")
yield
print("End")
yield
status = show_status()
next(status)
Start
Fungsi generator ini tidak memiliki loop. Sebaliknya, ia berisi tiga baris yang memiliki kata kunci yield. Kode membuat iterator generator status saat memanggil fungsi generator show_status(). Saat pertama kali program memanggil next(status), generator memulai eksekusi. Ia mencetak string "Start" dan berhenti setelah ekspresi yield pertama. Generator menghasilkan None karena tidak ada objek yang mengikuti kata kunci yield.
Program mencetak string "Middle" hanya ketika next() dipanggil untuk kedua kalinya:
next(status)
Middle
Generator berhenti setelah ekspresi yield kedua. Panggilan ketiga ke next() mencetak string terakhir, "End":
next(status)
End
Generator berhenti pada ekspresi yield terakhir. Ia akan menimbulkan pengecualian StopIteration saat program berikutnya meminta nilai dari iterator generator ini:
next(status)
Traceback (most recent call last):
...
StopIteration
Kita akan mengeksplorasi lebih banyak cara menggunakan generator di bagian berikutnya.
Fungsi generator membuat iterator generator, dan iterator bersifat iterable. Mari uraikan frasa ini. Setiap kali program memanggil fungsi generator, ia membuat sebuah iterator. Karena iterator bersifat iterable, ia dapat digunakan dalam loop for dan proses iteratif lainnya.
Karena itu, fungsi bawaan next() bukan satu-satunya cara untuk mengakses elemen dalam sebuah iterator. Bagian ini mengeksplorasi cara lain untuk bekerja dengan generator.
Mari tinjau kembali fungsi generator dari bagian sebelumnya dalam tutorial ini:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
for value in output:
print(value)
1
3
0
Alih-alih menggunakan next() beberapa kali, versi kode ini menggunakan iterator generator output dalam loop for. Karena iterator bersifat iterable, ia dapat digunakan dalam loop for. Loop mengambil item dari iterator generator hingga tidak ada nilai yang tersisa.
Tidak seperti struktur data seperti list dan tuple, sebuah iterator hanya dapat digunakan sekali. Kode tidak akan mencetak nilai lagi jika kita mencoba menjalankan loop for yang sama untuk kedua kalinya:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
print("First attempt:")
for value in output:
print(value)
print("Second attempt:")
for value in output:
print(value)
First attempt:
1
3
0
Second attempt:
Iterator habis oleh loop for pertama, sehingga tidak dapat lagi menghasilkan nilai. Jika generator dibutuhkan lagi setelah habis, kita harus membuat iterator generator baru dari fungsi generator.
Dimungkinkan juga memiliki beberapa iterator generator yang ada pada saat yang sama dalam sebuah program:
def find_letter_occurrences(words, letter):
for word in words:
yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
first_output = find_letter_occurrences(words, letter)
second_output = find_letter_occurrences(words, letter)
print("First value of first_output:")
print(next(first_output))
print("Values of second_output:")
for value in second_output:
print(value)
print("Remaining values of first_output:")
for value in first_output:
print(value)
First value of first_output:
1
Values of second_output:
1
3
0
Remaining values of first_output:
3
0
Fungsi generator find_letter_occurrences() membuat dua iterator generator: first_output dan second_output. Meskipun kedua iterator merujuk pada data yang sama dalam list words, keduanya berjalan secara independen satu sama lain.
Contoh ini mengambil nilai pertama dari first_output menggunakan next(). Iterator generator menghasilkan 1 dan berhenti sejenak pada titik ini. Program selanjutnya melakukan loop melalui second_output. Karena generator ini belum menghasilkan nilai apa pun, loop akan melalui semua nilai yang dihasilkan oleh iterator kedua. Terakhir, ada loop for lain yang mengiterasi first_output. Namun, iterator ini sudah menghasilkan nilai pertamanya lebih awal dalam program. Loop berjalan melalui nilai-nilai yang tersisa dalam iterator ini.
Loop for bukan satu-satunya proses yang dapat digunakan untuk mengiterasi iterator generator:
print(*find_letter_occurrences(words, letter))
print(sorted(find_letter_occurrences(words, letter)))
1 3 0
[0, 1, 3]
Pada contoh ini, program memanggil fungsi generator secara langsung untuk membuat dan menggunakan iterator generator alih-alih menugaskannya ke variabel. Pada contoh pertama, iterator di-unpack menggunakan notasi bintang. Proses ini mengandalkan protokol iterasi yang sama seperti loop for.
Pada contoh kedua, iterator generator diteruskan ke fungsi bawaan sorted(), yang memerlukan argumen iterable. Generator bersifat iterable, sehingga dapat digunakan kapan pun iterasi Python terjadi.
Sebuah generator menghasilkan nilai dan berhenti sejenak hingga nilai berikutnya dibutuhkan. Setiap kali kode meminta nilai dari sebuah iterator, kode di dalam fungsi generator akan dieksekusi hingga ekspresi yield berikutnya dievaluasi. Pada semua contoh dalam tutorial sejauh ini, fungsi generator memiliki jumlah ekspresi yield yang terbatas. Namun, dimungkinkan untuk membuat generator yang menghasilkan jumlah nilai tak terbatas dengan menggunakan loop while dalam fungsi generator. Pada contoh berikut, generator menghasilkan warna acak dari list warna yang diteruskan ke fungsi generator:
import random
def get_color(colors):
while True:
yield random.choice(colors)
output_colors = get_color(["red", "green", "blue"])
print("First two colors:")
print(next(output_colors))
print(next(output_colors))
print("Next 10 colors using a 'for' loop:")
for _ in range(10):
print(next(output_colors))
First two colors:
green
red
Next 10 colors using a 'for' loop:
blue
green
green
green
red
red
red
blue
green
red
Fungsi generator get_color() memiliki ekspresi yield di dalam loop while. Oleh karena itu, kode akan selalu menemui ekspresi yield lain saat mencari nilai berikutnya. Iterator generator output_colors menghasilkan jumlah warna tak terbatas yang dipilih secara acak dari list masukan. Generator ini tidak akan pernah habis.
Tidak mungkin membuat struktur data tak hingga seperti list dan tuple. Generator memungkinkan program membuat iterable tak hingga. Perhatikan bahwa jika iterator generator digunakan langsung dalam loop for, loop akan berjalan selamanya.
Generator memiliki kasus penggunaan yang lebih lanjut di Python. Bagian ini akan mengeksplorasi beberapa di antaranya.
Generator juga dapat menerima data tambahan yang dapat digunakan saat mengevaluasi kode. Pernyataan yang berisi kata kunci yield adalah sebuah ekspresi yang dievaluasi menjadi sebuah nilai. Nilai ini dapat ditetapkan ke variabel di dalam fungsi generator. Mari mulai dengan contoh dasar untuk menunjukkan konsep ini:
def generator_function():
value = yield 1
print(f"The yield expression evaluates to: {value}")
value = yield 2
print(f"The yield expression evaluates to: {value}")
output = generator_function()
print(next(output))
print(next(output))
print(next(output))
1
The yield expression evaluates to: None
2
The yield expression evaluates to: None
Traceback (most recent call last):
...
StopIteration
Kata kunci yield di Python membuat sebuah ekspresi yang dievaluasi menjadi sebuah nilai. Namun, nilai ekspresi ini di dalam fungsi generator bukanlah objek yang sama dengan yang dihasilkan oleh generator. Pertimbangkan ekspresi yield yang pertama. Generator menghasilkan bilangan bulat 1. Oleh karena itu, print(next(output)) menampilkan 1 saat pertama kali dipanggil dan menghentikan eksekusi generator.
Namun, ekspresi yield di dalam generator dievaluasi menjadi sebuah objek, yang kodenya ditetapkan ke nama variabel value. Dalam contoh ini, yield menetapkan None ke value. Proses ini diulangi untuk kemunculan kedua yield dalam fungsi generator. Tujuan dari panggilan next() yang ketiga adalah memastikan semua kode dalam fungsi generator dieksekusi.
Mari ganti panggilan next() kedua dan ketiga dengan .send(), yang merupakan metode dalam kelas generator:
def generator_function():
value = yield 1
print(f"The yield expression evaluates to: {value}")
value = yield 2
print(f"The yield expression evaluates to: {value}")
output = generator_function()
print(next(output))
print(output.send("Here's a value"))
print(output.send("Here's another value"))
1
The yield expression evaluates to: Here's a value
2
The yield expression evaluates to: Here's another value
Traceback (most recent call last):
...
StopIteration
Fungsi generatornya tidak berubah. Generator dimulai dengan memanggil next(), dan kodenya berjalan hingga menghasilkan bilangan bulat pertama, 1. Alih-alih menggunakan next() untuk kedua kalinya, program memanggil output.send(). Metode ini mengirimkan sebuah objek ke dalam generator. Pada contoh ini, objeknya adalah sebuah string. Ekspresi yield dalam fungsi generator dievaluasi menjadi string ini, yang ditetapkan ke value. Karena itu, generator dapat menggunakan string tersebut di dalam kodenya.
Panggilan kedua ke .send() mengirimkan objek baru ke generator, yang ditetapkan ke variabel yang sama, value. Generator memunculkan StopIteration setelah pemanggilan print() terakhir karena tidak ada lagi ekspresi yield.
Mari lihat contoh lain menggunakan .send(). Generator berikut menampilkan saldo dalam sebuah akun, dan saldonya dapat diperbarui:
def get_balance(start_balance):
balance = start_balance
while True:
amount = yield balance
if amount is not None:
balance += amount
current_balance = get_balance(100)
print(next(current_balance))
print(current_balance.send(10))
print(current_balance.send(-20))
print(next(current_balance))
100
110
90
90
Fungsi generator memerlukan saldo awal saat dipanggil. Nilai balance dapat berubah seiring generator mengeksekusi kode. Objek apa pun yang dikirim ke generator menggunakan .send() akan ditetapkan ke amount. Variabel ini akan bernilai None jika generator melakukan yield tanpa ada objek yang dikirim ke sana, atau akan berisi objek yang dikirim menggunakan .send().
Iterator generator current_balance dimulai dengan saldo $100. Generator dimulai dengan memanggil next(), yang mengeksekusi kode hingga nilai pertama di-yield.
Setelah generator dimulai, eksekusi dapat dilanjutkan menggunakan .send() alih-alih next(). Generator menambahkan nilai yang dikirim ke saldo. Jika tidak ada nilai yang dikirim, misalnya dengan memanggil next() lagi, generator akan melakukan yield saldo yang tidak berubah.
Generator Python juga dapat melakukan yield nilai secara langsung dari generator atau iterable lain menggunakan sintaks yield from. Mari lihat contoh fungsi generator yang melakukan yield nilai dari daftar bertingkat:
def flatten(nested_list):
for item in nested_list:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
nested_list = [1, [2, 3], [4, [5, 6]], 7]
print(list(flatten(nested_list)))
[1, 2, 3, 4, 5, 6, 7]
Fungsi generator menerima sebuah daftar, yang dapat mencakup daftar bersarang di dalamnya. Loop for mengiterasi item-item dalam daftar. Setiap item pada daftar terluar bisa berupa sebuah nilai, dalam hal ini sebuah bilangan bulat, atau daftar lain. Saat item bukan sebuah daftar, generator melakukan yield item tersebut.
Namun, ketika item adalah sebuah daftar, generator secara rekursif memanggil fungsi generator flatten() lagi dengan daftar dalam sebagai argumen. Ini membuat iterator generator lain, yang menggunakan daftar dalam sebagai sumber datanya. Jika baris ini menggunakan ekspresi yield, generator pertama akan melakukan yield generator kedua. Sebaliknya, dengan menggunakan yield from, generator pertama melakukan yield nilai-nilai dari generator kedua.
yield vs. returnDefinisi fungsi dengan return dan yield terlihat mirip, tetapi perilakunya berbeda. Mari kita rangkum perbedaan utamanya:
|
Fungsi Biasa |
Fungsi Generator |
|
|
Kata Kunci |
return (implisit jika tidak digunakan secara eksplisit) |
yield |
|
Saat Dipanggil |
Mengeksekusi kode hingga mencapai return, lalu mengembalikan nilai akhir |
Membuat sebuah iterator generator |
|
Terminasi |
Diakhiri oleh pernyataan return |
Dijeda oleh yield, dapat dilanjutkan kemudian |
|
Nilai Kembalian |
Objek tunggal (dapat berupa struktur data) |
Iterator generator |
|
Ekspresi Yield |
Tidak berlaku (membuat sebuah pernyataan) |
Dievaluasi menjadi None atau nilai yang dikirim menggunakan .send() |
|
Kasus Penggunaan |
Ideal untuk mengembalikan hasil akhir |
Ideal untuk membuat aliran data, terutama deret besar atau tak hingga |
Kata kunci yield Python digunakan dalam fungsi untuk mendefinisikan fungsi generator. Saat dipanggil, fungsi-fungsi ini membuat iterator generator. Generator adalah contoh evaluasi malas (lazy evaluation) di Python, di mana ekspresi dievaluasi ketika nilainya dibutuhkan alih-alih saat mengeksekusi ekspresi tersebut. Karena itu, ekspresi yield berguna untuk membuat aliran data di mana nilai-nilai dihasilkan sesuai permintaan tanpa perlu menyimpannya di memori.
Pertimbangan efisiensi penting saat menangani set data besar yang memerlukan banyak operasi. Iterator generator Python adalah salah satu alat utama yang dibutuhkan untuk memanipulasi data dalam jumlah besar secara efisien.
Jika Anda ingin mempelajari lebih lanjut tentang Python, lihat jalur karier Python Developer ini.
Saya belajar Fisika dan Matematika pada tingkat sarjana di University of Malta. Kemudian, saya pindah ke London dan meraih gelar PhD dalam bidang Fisika dari Imperial College. Saya meneliti teknik optik baru untuk memindai retina manusia. Sekarang, saya berfokus menulis tentang Python, berkomunikasi tentang Python, dan mengajar Python.
Belajar Python dengan kursus-kursus ini!
Kursus
Kursus
Program
blogs
Hugo Bowne-Anderson
13 mnt
blogs
David Woods
13 mnt

blogs
Javier Canales Luna
14 mnt
blogs
Dario Radečić
15 mnt