Penjana dalam Python
Artikel ini menerangkan penjana dalam Python.
YouTube Video
Penjana dalam Python
Gambaran keseluruhan
Penjana dalam Python ialah sejenis iterator dan satu ciri yang berkuasa untuk melakukan pemprosesan berulang secara cekap. Ia membolehkan anda menulis kod yang cekap dari segi memori apabila berurusan dengan sejumlah besar data.
Apakah itu Penjana?
Penjana dalam Python ialah fungsi khas yang menghasilkan satu nilai pada satu masa, ditakrifkan dengan kata kunci yield
. Ciri utamanya ialah ia menghentikan pelaksanaan sambil mengekalkan keadaannya dan boleh disambung semula kemudian.
Asas yield
yield
ialah kata kunci yang memulangkan nilai dan menghentikan pelaksanaan fungsi pada masa yang sama.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8print(next(gen)) # 1
9print(next(gen)) # 2
10print(next(gen)) # 3
- Apabila dipanggil, fungsi ini memulangkan objek penjana yang mengeluarkan nilai satu persatu.
- Jika anda memanggil
next()
apabila tiada nilai seterusnya, ralatStopIteration
akan berlaku.
next()
dan StopIteration
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8try:
9 while True:
10 value = next(gen)
11 print(value)
12except StopIteration:
13 print("Finished")
- Dengan mengendalikan ralat
StopIteration
secara eksplisit seperti ini, anda boleh mengesan bila penjana telah selesai.
send(value)
Memanggil send(value)
menyambung semula penjana dan menghantar value
ke kedudukan ungkapan yield
. Nilai yang dihantar boleh diterima di pihak penjana sebagai nilai pulangan bagi ungkapan yield
. Pada panggilan pertama, anda tidak boleh menghantar apa-apa selain None
dengan send(value)
, jadi anda mesti menggunakan next()
atau send(None)
.
1def gen():
2 x = yield 1
3 print(f"x = {x}")
4 y = yield 2
5 print(f"y = {y}")
6
7g = gen()
8print(next(g)) # -> 1 (value from yield 1)
9print(g.send(10)) # -> x = 10, 2 (value from yield 2)
10print(g.send(20)) # -> y = 20, StopIteration occurs
- Dengan
send(10)
,yield
dalam penjana menjadi ungkapan yang mengembalikan 10, dan 10 diberikan kepadax
.
throw()
Memanggil throw
menyambung semula penjana dan melempar pengecualian pada kedudukan yield
yang dihentikan sementara. Anda boleh mengendalikan pengecualian di dalam penjana untuk meneruskan pemprosesan. Jika pengecualian tidak ditangkap, ia akan tersebar keluar dan penjana akan berakhir.
1def gen():
2 try:
3 yield 1
4 except ValueError as e:
5 print(f"Caught: {e}")
6 yield "recovered"
7
8g = gen()
9print(next(g)) # -> 1
10print(g.throw(ValueError("boom"))) # -> Caught: boom, "recovered"
- Dalam kod ini,
throw
dipanggil untuk menyuntik pengecualian ke dalam penjana. Di pihak penjana, pengecualian dikendalikan danrecovered
dipulangkan.
close()
Memanggil close()
akan menamatkan penjana. Di dalam penjana, anda boleh menjalankan pembersihan menggunakan finally
. Memanggil next()
atau send()
selepas memanggil close()
akan menimbulkan ralat StopIteration
.
1def gen():
2 try:
3 yield 1
4 finally:
5 print("Cleaning up...")
6
7g = gen()
8print(next(g)) # -> 1
9g.close() # -> Cleaning up...
- Kod ini menunjukkan bahawa memanggil
close()
akan menamatkan penjana dan mencetuskan proses pembersihan dalamfinally
.
yield from
yield from
ialah sintaks yang digunakan untuk mendelegasikan kepada subpenjana. Ia adalah cara mudah untuk memanggil penjana lain di dalam penjana dan menyalurkan semua nilainya ke skop luar.
1def sub_gen():
2 yield 1
3 yield 2
4
5def main_gen():
6 yield from sub_gen()
7 yield 3
8
9print(list(main_gen())) # -> [1, 2, 3]
- Kod ini mendelegasikan semua nilai daripada subpenjana kepada penjana luar menggunakan
yield from
, dan kemudian menghasilkan3
.
Hubungan dengan Iterator
Penjana melaksanakan __iter__()
dan __next__()
secara dalaman, menjadikan mereka sejenis iterator. Oleh itu, ia serasi sepenuhnya dengan operasi boleh ulang seperti gelung for
.
Integrasi dengan Gelung for
Dalam Python, gelung for
menggunakan next()
secara dalaman untuk mendapatkan nilai secara automatik.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6for value in simple_generator():
7 print(value)
Dengan kaedah ini, pengendalian StopIteration
juga dilakukan secara automatik.
Mencipta Penjana Tak Terhingga
1def count_up(start=0):
2 while True:
3 yield start
4 start += 1
5
6counter = count_up()
7print(next(counter)) # 0
8print(next(counter)) # 1
Adalah mungkin untuk mencipta gelung tak terhingga, tetapi anda perlu berhati-hati apabila menggunakannya.
Ekspresi Penjana
Ekspresi penjana, yang ditulis menggunakan tanda kurung, membolehkan anda mentakrifkan penjana dengan sintaks yang serupa dengan pemahaman senarai.
1# List comprehension (generates the entire list at once)
2squares_list = [x**2 for x in range(5)]
3print(squares_list)
4
5# Generator expression
6squares_gen = (x**2 for x in range(5))
7for square in squares_gen:
8 print(square)
Tidak seperti pemahaman senarai, ia tidak memuatkan semua elemen ke dalam memori serentak, menjadikannya lebih cekap dari segi penggunaan memori.
Pengendalian Ralat dalam Penjana
Pengecualian boleh berlaku di dalam penjana. Dalam kes sedemikian, anda menggunakan try-except
sama seperti dalam kod Python biasa.
1def safe_divide_generator(numbers, divisor):
2 """Yields results of dividing numbers by a given divisor safely."""
3 for number in numbers:
4 try:
5 yield number / divisor # Attempt to divide and yield result.
6 except ZeroDivisionError:
7 yield float('inf') # Return infinity if division by zero occurs.
8
9# Example usage
10numbers = [10, 20, 30]
11gen = safe_divide_generator(numbers, 0) # Create generator with divisor as 0.
12for value in gen:
13 print(value) # Output: inf, inf, inf
Dalam contoh ini, pengendalian ralat yang betul dilakukan jika berlaku pembahagian dengan sifar.
Jejak Tindanan Penjana
Jika pengecualian berlaku di dalam penjana, ia akan dinaikkan apabila penjana disambung semula.
1def error_generator():
2 """A generator that yields values and raises an error."""
3 yield 1
4 raise ValueError("An error occurred") # Raise a ValueError intentionally.
5 yield 2
6
7gen = error_generator()
8print(next(gen)) # Output: 1 (first value yielded)
9try:
10 print(next(gen)) # Attempt to get the next value, which raises an error
11except ValueError as e:
12 print(e) # Output: An error occurred (exception message is printed)
- Penjana ini mengembalikan
1
terlebih dahulu. Ralat yang berlaku semasa menyambung semula ditangkap dan dipaparkan sebagai mesej ralat.
Contoh Penggunaan Penjana
Membaca fail baris demi baris (sesuai untuk fail yang besar)
1def read_large_file(filepath):
2 with open(filepath, 'r') as f:
3 for line in f:
4 yield line.strip()
- Fungsi ini membaca fail teks baris demi baris menggunakan iterator, memotong ruang kosong dari setiap baris, dan mengembalikannya sebagai penjana, membolehkan fail besar diproses dengan penggunaan memori yang rendah.
Penjana untuk Jujukan Fibonacci
1def fibonacci(limit):
2 a, b = 0, 1
3 while a < limit:
4 yield a
5 a, b = b, a + b
6
7for n in fibonacci(100):
8 print(n)
- Kod ini menggunakan penjana untuk menjana nombor Fibonacci secara berturutan yang kurang dari had atas dan mengeluarkannya menggunakan gelung
for
.
Kes Penggunaan
Penjana juga boleh digunakan dalam senario berikut.
- Pemprosesan berurutan fail CSV atau log yang besar
- Penomboran API
- Memproses data penstriman (contohnya, Kafka, peranti IoT)
Ringkasan
Konsep | Perkara Utama |
---|---|
yield |
Memberhentikan dan memulangkan nilai |
Fungsi Penjana | Fungsi yang mengandungi yield dan memulangkan iterator apabila dipanggil |
Kelebihan | Cekap memori dan sesuai untuk pemprosesan set data yang besar |
Ekspresi Penjana | Membenarkan sintaks ringkas seperti (x for x in iterable) |
Dengan menggunakan penjana, anda boleh memproses set data yang besar dengan cekap sambil menjimatkan memori dan memastikan kod anda ringkas.
Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.