Mga Generator sa Python

Mga Generator sa Python

Ipinaliwanag ng artikulong ito ang mga generator sa Python.

YouTube Video

Mga Generator sa Python

Pangkalahatang-ideya

Ang mga generator ng Python ay isang uri ng iterator at isang kapangyarihang tampok para sa mabisang pagsasagawa ng paulit-ulit na proseso. Pinapayagan ka nitong sumulat ng memory-efficient na code kapag humaharap sa malaking dami ng data.

Ano ang Generator?

Ang generator sa Python ay isang espesyal na function na nagpo-produce ng isang halaga kada tawag, at tinutukoy gamit ang yield na keyword. Ang katangian nito ay na napapahinto ang execution habang naiingatan ang estado nito, at maaaring ipagpatuloy sa kalaunan.

Pangunahing Kaalaman ukol sa yield

Ang yield ay isang keyword na nagbabalik ng halaga at sabay na pinapahinto ang pagsasagawa ng function.

 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
  • Kapag tinawag, ang function na ito ay nagbabalik ng generator object na nagbibigay ng mga halaga isa-isa.
  • Kapag tinawag mo ang next() kapag wala nang susunod na halaga, magaganap ang isang StopIteration na error.

next() at 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")
  • Sa pamamagitan ng hayagang paghawak sa error na StopIteration gaya nito, maaari mong matukoy kung kailan natapos ang isang generator.

send(value)

Ang pagtawag ng send(value) ay nagpapatuloy sa generator at ipinapadala ang value sa posisyon ng yield expression. Ang ipinadalang halaga ay maaaring matanggap sa panig ng generator bilang return value ng yield expression. Sa unang tawag, hindi ka maaaring magpadala ng anuman maliban sa None gamit ang send(value), kaya kailangan mong gamitin ang next() o 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
  • Sa paggamit ng send(10), ang yield ng generator ay nagiging expression na nagbabalik ng 10, at 10 ang ina-assign sa x.

throw()

Ang pagtawag ng throw ay nagpapatuloy sa generator at nagbuo ng exception sa posisyon ng naka-pause na yield. Maari mong hawakan ang exception sa loob ng generator para maipagpatuloy ang pagproseso. Kung ang exception ay hindi nahuli, ito ay lalabas palabas at matatapos ang generator.

 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"
  • Sa code na ito, tinatawag ang throw para magpasok ng exception sa generator. Sa panig ng generator, hinahawakan ang exception at recovered ang ibinabalik.

close()

Ang pagtawag ng close() ay nagtatapos sa generator. Sa loob ng generator, maaari kang magsagawa ng paglilinis gamit ang finally. Ang pagtawag ng next() o send() pagkatapos ng close() ay magbubunga ng StopIteration error.

1def gen():
2    try:
3        yield 1
4    finally:
5        print("Cleaning up...")
6
7g = gen()
8print(next(g))  # -> 1
9g.close()       # -> Cleaning up...
  • Ipinapakita ng code na ito na ang pagtawag ng close() ay nagtatapos sa generator at nagpapagana ng proseso ng paglilinis sa finally.

yield from

yield from ay isang syntax na ginagamit para magdelega sa isang subgenerator. Ito ay isang simpleng paraan para tumawag ng ibang generator sa loob ng isang generator at ipasa ang lahat ng mga halaga nito sa panlabas na scope.

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]
  • Ang code na ito ay dine-delegate ang lahat ng halaga mula sa subgenerator papunta sa outer generator gamit ang yield from, at pagkatapos ay nag-yi-yield ng 3.

Kaugnayan sa mga Iterator

Ang mga generator ay may sariling implementasyon ng __iter__() at __next__(), kaya itinuturing silang isang uri ng iterator. Dahil dito, ganap silang compatible sa mga iterable operations gaya ng for loop.

Integrasyon sa mga for Loop

Sa Python, ang for loop ay gumagamit ng next() sa loob upang awtomatikong makuha ang mga halaga.

1def simple_generator():
2    yield 1
3    yield 2
4    yield 3
5
6for value in simple_generator():
7    print(value)

Sa pamamaraang ito, awtomatiko ring hinahawakan ang StopIteration.

Paggawa ng Walang-hanggang Generator

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

Maaaring gumawa ng mga walang-hanggang loop, ngunit kailangang mag-ingat sa paggamit nito.

Mga Generator Expression

Ang mga generator expression na sinusulat gamit ang panaklong ay nagbibigay-daan upang makabuo ng generator gamit ang syntax na kahawig ng list comprehension.

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)

Hindi tulad ng list comprehensions, hindi nila niloload ang lahat ng elemento sa memorya nang sabay-sabay, kaya mas memory efficient ito.

Paghawak ng Error sa Mga Generator

Maaaring may error na mangyari sa loob ng generator. Sa ganitong mga pagkakataon, gumamit ng try-except gaya ng regular na Python code.

 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

Sa halimbawang ito, wasto ang paghawak ng error sa kaganapan ng paghahati sa zero.

Stack Trace ng Generator

Kung may nangyaring exception sa loob ng generator, ito ay ilalabas kapag nagpapatuloy ang generator.

 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)
  • Ang generator na ito ay nagbabalik muna ng 1. Ang error na lumitaw sa pagpapatuloy ay nasasalo at ipinapakita bilang mensahe ng error.

Mga Halimbawa ng Paggamit ng Generator

Pagbasa ng file kada linya (angkop para sa malalaking file)

1def read_large_file(filepath):
2    with open(filepath, 'r') as f:
3        for line in f:
4            yield line.strip()
  • Ang function na ito ay nagbabasa ng isang text file linya kada linya gamit ang iterator, tinatanggal ang whitespace sa bawat linya, at ibinabalik ito bilang generator, kaya maaaring maproseso ang malalaking file na mababa lang ang konsumo sa memorya.

Generator para sa Fibonacci Sequence

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)
  • Gumagamit ang code na ito ng generator upang sunud-sunod na bumuo ng mga Fibonacci na numero na mas mababa sa itinatakdang limitasyon at inilalabas ang mga ito gamit ang isang for loop.

Mga Gamit / Use Case

Ang mga generator ay maaari ring magamit sa mga sumusunod na sitwasyon.

  • Sunud-sunod na pagproseso ng malalaking CSV o log na mga file
  • Pagination sa API
  • Pagpoproseso ng streaming data (halimbawa, Kafka, mga IoT device)

Buod

Konsepto Mahalagang Punto
yield Pinapahinto at nagbabalik ng halaga
Generator Function Function na may yield at nagbabalik ng iterator kapag tinawag
Kalamangan Memory-efficient at mainam para sa malalaking set ng data
Generator Expression Nagbibigay-daan sa pinaikling syntax tulad ng (x for x in iterable)

Sa paggamit ng mga generator, maaari mong iproseso ang malalaking set ng data nang mahusay habang natitipid ang memorya at nananatiling maikli ang iyong code.

Maaari mong sundan ang artikulo sa itaas gamit ang Visual Studio Code sa aming YouTube channel. Paki-check din ang aming YouTube channel.

YouTube Video