Dalam tutorial ini, Anda akan belajar cara membuat iterasi dengan mudah menggunakan generator Python, perbedaannya dari iterator dan fungsi normal, dan mengapa Anda harus menggunakannya.
Video: Generator Python
Generator dengan Python
Ada banyak pekerjaan dalam membangun iterator dengan Python. Kita harus mengimplementasikan kelas dengan metode __iter__()
dan __next__()
, melacak status internal, dan menaikkan StopIteration
ketika tidak ada nilai yang akan dikembalikan.
Ini panjang dan berlawanan dengan intuisi. Generator datang untuk menyelamatkan dalam situasi seperti itu.
Generator Python adalah cara sederhana untuk membuat iterator. Semua pekerjaan yang kami sebutkan di atas secara otomatis ditangani oleh generator dengan Python.
Sederhananya, generator adalah fungsi yang mengembalikan objek (iterator) yang dapat kita iterasi (satu nilai pada satu waktu).
Buat Generator dengan Python
Cukup sederhana untuk membuat generator dengan Python. Ini semudah mendefinisikan fungsi normal, tetapi dengan yield
pernyataan, bukan return
pernyataan.
Jika suatu fungsi berisi setidaknya satu yield
pernyataan (mungkin berisi pernyataan yield
atau lainnya return
), itu menjadi fungsi generator. Keduanya yield
dan return
akan mengembalikan beberapa nilai dari suatu fungsi.
Perbedaannya adalah bahwa ketika sebuah return
pernyataan menghentikan suatu fungsi seluruhnya, yield
pernyataan itu menjeda fungsi tersebut dan menyimpan semua statusnya dan kemudian melanjutkan dari sana pada panggilan yang berurutan.
Perbedaan antara fungsi Generator dan fungsi Normal
Berikut adalah perbedaan fungsi generator dari fungsi normal.
- Fungsi generator berisi satu atau lebih
yield
pernyataan. - Saat dipanggil, ia mengembalikan objek (iterator) tetapi tidak segera memulai eksekusi.
- Metode suka
__iter__()
dan__next__()
diimplementasikan secara otomatis. Jadi kita bisa melakukan iterasi melalui item menggunakannext()
. - Setelah fungsi menghasilkan, fungsi tersebut dihentikan sementara dan kontrol ditransfer ke pemanggil.
- Variabel lokal dan statusnya diingat di antara panggilan yang berurutan.
- Akhirnya, ketika fungsi berakhir,
StopIteration
dimunculkan secara otomatis pada panggilan selanjutnya.
Berikut adalah contoh untuk menggambarkan semua poin yang disebutkan di atas. Kami memiliki fungsi generator bernama my_gen()
dengan beberapa yield
pernyataan.
# A simple generator function def my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n
Proses interaktif di interpreter diberikan di bawah ini. Jalankan ini di shell Python untuk melihat hasilnya.
>>> # It returns an object but does not start execution immediately. >>> a = my_gen() >>> # We can iterate through the items using next(). >>> next(a) This is printed first 1 >>> # Once the function yields, the function is paused and the control is transferred to the caller. >>> # Local variables and theirs states are remembered between successive calls. >>> next(a) This is printed second 2 >>> next(a) This is printed at last 3 >>> # Finally, when the function terminates, StopIteration is raised automatically on further calls. >>> next(a) Traceback (most recent call last):… StopIteration >>> next(a) Traceback (most recent call last):… StopIteration
Satu hal yang menarik untuk diperhatikan dalam contoh di atas adalah bahwa nilai variabel n diingat di antara setiap panggilan.
Tidak seperti fungsi normal, variabel lokal tidak dimusnahkan saat fungsi menghasilkan. Selanjutnya, objek generator hanya dapat diiterasi satu kali.
Untuk memulai kembali proses kita perlu membuat objek generator lain menggunakan sesuatu seperti a = my_gen()
.
Satu hal terakhir yang perlu diperhatikan adalah kita dapat menggunakan generator dengan for loop secara langsung.
Ini karena sebuah for
loop mengambil sebuah iterator dan mengulanginya menggunakan next()
fungsi. Ini secara otomatis berakhir saat StopIteration
dinaikkan. Periksa di sini untuk mengetahui bagaimana perulangan for sebenarnya diimplementasikan dengan Python.
# A simple generator function def my_gen(): n = 1 print('This is printed first') # Generator function contains yield statements yield n n += 1 print('This is printed second') yield n n += 1 print('This is printed at last') yield n # Using for loop for item in my_gen(): print(item)
Saat Anda menjalankan program, hasilnya adalah:
Ini dicetak pertama 1 Ini dicetak kedua 2 Ini dicetak terakhir 3
Generator Python dengan Loop
Contoh di atas kurang bermanfaat dan kami mempelajarinya hanya untuk mendapatkan gambaran tentang apa yang terjadi di latar belakang.
Biasanya, fungsi generator diimplementasikan dengan loop yang memiliki kondisi penghentian yang sesuai.
Mari kita ambil contoh generator yang membalikkan string.
def rev_str(my_str): length = len(my_str) for i in range(length - 1, -1, -1): yield my_str(i) # For loop to reverse the string for char in rev_str("hello"): print(char)
Keluaran
olleh
Dalam contoh ini, kami telah menggunakan range()
fungsi untuk mendapatkan indeks dalam urutan terbalik menggunakan perulangan for.
Catatan : Fungsi generator ini tidak hanya berfungsi dengan string, tetapi juga dengan jenis iterable lain seperti list, tuple, dll.
Ekspresi Generator Python
Generator sederhana dapat dengan mudah dibuat dengan cepat menggunakan ekspresi generator. Itu membuat generator bangunan menjadi mudah.
Mirip dengan fungsi lambda yang membuat fungsi anonim, ekspresi generator membuat fungsi generator anonim.
Sintaks untuk ekspresi generator mirip dengan pemahaman daftar di Python. Tetapi tanda kurung siku diganti dengan tanda kurung bulat.
Perbedaan utama antara pemahaman daftar dan ekspresi generator adalah bahwa pemahaman daftar menghasilkan seluruh daftar sementara ekspresi generator menghasilkan satu item dalam satu waktu.
They have lazy execution ( producing items only when asked for ). For this reason, a generator expression is much more memory efficient than an equivalent list comprehension.
# Initialize the list my_list = (1, 3, 6, 10) # square each term using list comprehension list_ = (x**2 for x in my_list) # same thing can be done using a generator expression # generator expressions are surrounded by parenthesis () generator = (x**2 for x in my_list) print(list_) print(generator)
Output
(1, 9, 36, 100)
We can see above that the generator expression did not produce the required result immediately. Instead, it returned a generator object, which produces items only on demand.
Here is how we can start getting items from the generator:
# Initialize the list my_list = (1, 3, 6, 10) a = (x**2 for x in my_list) print(next(a)) print(next(a)) print(next(a)) print(next(a)) next(a)
When we run the above program, we get the following output:
1 9 36 100 Traceback (most recent call last): File "", line 15, in StopIteration
Generator expressions can be used as function arguments. When used in such a way, the round parentheses can be dropped.
>>> sum(x**2 for x in my_list) 146 >>> max(x**2 for x in my_list) 100
Use of Python Generators
There are several reasons that make generators a powerful implementation.
1. Easy to Implement
Generators can be implemented in a clear and concise way as compared to their iterator class counterpart. Following is an example to implement a sequence of power of 2 using an iterator class.
class PowTwo: def __init__(self, max=0): self.n = 0 self.max = max def __iter__(self): return self def __next__(self): if self.n> self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result
The above program was lengthy and confusing. Now, let's do the same using a generator function.
def PowTwoGen(max=0): n = 0 while n < max: yield 2 ** n n += 1
Since generators keep track of details automatically, the implementation was concise and much cleaner.
2. Memory Efficient
A normal function to return a sequence will create the entire sequence in memory before returning the result. This is an overkill, if the number of items in the sequence is very large.
Generator implementation of such sequences is memory friendly and is preferred since it only produces one item at a time.
3. Represent Infinite Stream
Generator adalah media yang sangat baik untuk mewakili aliran data yang tidak terbatas. Aliran tak terbatas tidak dapat disimpan dalam memori, dan karena generator hanya menghasilkan satu item pada satu waktu, mereka dapat mewakili aliran data tak terbatas.
Fungsi generator berikut dapat menghasilkan semua bilangan genap (setidaknya dalam teori).
def all_even(): n = 0 while True: yield n n += 2
4. Generator Pipelining
Beberapa generator dapat digunakan untuk menyalurkan serangkaian operasi. Ini paling baik diilustrasikan dengan menggunakan contoh.
Misalkan kita memiliki generator yang menghasilkan angka dalam deret Fibonacci. Dan kami memiliki generator lain untuk mengkuadratkan angka.
Jika kita ingin mengetahui jumlah kuadrat angka dalam deret Fibonacci, kita bisa melakukannya dengan cara berikut ini dengan memipeline output dari fungsi generator secara bersamaan.
def fibonacci_numbers(nums): x, y = 0, 1 for _ in range(nums): x, y = y, x+y yield x def square(nums): for num in nums: yield num**2 print(sum(square(fibonacci_numbers(10))))
Keluaran
4895
Pipelining ini efisien dan mudah dibaca (dan ya, jauh lebih keren!).