Hasil Python, Generator dan Ekspresi Generator

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 StopIterationketika 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 yieldpernyataan, bukan returnpernyataan.

Jika suatu fungsi berisi setidaknya satu yieldpernyataan (mungkin berisi pernyataan yieldatau lainnya return), itu menjadi fungsi generator. Keduanya yielddan returnakan mengembalikan beberapa nilai dari suatu fungsi.

Perbedaannya adalah bahwa ketika sebuah returnpernyataan menghentikan suatu fungsi seluruhnya, yieldpernyataan 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 yieldpernyataan.
  • 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 menggunakan next().
  • 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, StopIterationdimunculkan 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 yieldpernyataan.

 # 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 forloop mengambil sebuah iterator dan mengulanginya menggunakan next()fungsi. Ini secara otomatis berakhir saat StopIterationdinaikkan. 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!).

Artikel yang menarik...