Generators
generator คืออะไร?
หัวข้อที่มีชื่อว่า “generator คืออะไร?”generator คือ function ที่สามารถหยุดการทำงานชั่วคราว แล้วกลับมาทำงานต่อจากจุดที่หยุดไว้ได้ แทนที่จะคำนวณค่าทั้งหมดแล้ว return ออกมาเป็น list generator จะผลิตค่าออกมา ทีละค่า เฉพาะเมื่อถูกร้องขอเท่านั้น สิ่งนี้เรียกว่า lazy evaluation
หัวใจสำคัญคือ keyword yield
เมื่อ Python เจอ yield จะหยุด function ชั่วคราว ส่งค่ากลับไปให้ผู้เรียก แล้วแช่แข็ง state ภายในไว้ (ตัวแปรต่าง ๆ และตำแหน่งของ instruction pointer)
การเรียก next() ครั้งถัดไปจะทำงานต่อจากบรรทัดนั้นเป๊ะ ๆ
def count_up(start: int, stop: int): current = start while current <= stop: yield current # pause here and emit current current += 1 # resume here on the next next() call
gen = count_up(1, 4)print(next(gen)) # 1print(next(gen)) # 2print(next(gen)) # 3print(next(gen)) # 4# next(gen) would now raise StopIterationgenerator function เทียบกับ function ปกติ
หัวข้อที่มีชื่อว่า “generator function เทียบกับ function ปกติ”| ประเด็น | function ปกติ | generator function |
|---|---|---|
| Return | ครั้งเดียว ด้วย return | หลายครั้ง ด้วย yield |
| State | ถูกทิ้งหลัง return | ถูกแช่แข็งไว้ระหว่าง yield |
| Memory | สร้างผลลัพธ์ทั้งก้อน | ทีละค่า |
| Type | คือค่าที่ return | เป็น object ชนิด generator |
function จะกลายเป็น generator function ทันทีที่มีคำสั่ง yield แม้เพียงตัวเดียว
การเรียก generator function จะไม่ทำให้โค้ดทำงานเลย แต่จะ return generator object ออกมาทันที
iter() และ next()
หัวข้อที่มีชื่อว่า “iter() และ next()”ทุก generator object ทำตาม iterator protocol นั่นคือมี method __iter__() และ __next__()
next(gen)จะเลื่อนไปยังyieldตัวถัดไปและ return ค่านั้นออกมา- เมื่อไม่มีค่าเหลือแล้ว
next(gen)จะ raiseStopIteration - ลูป
forจะเรียกnext()ให้อัตโนมัติ และจับStopIterationเพื่อหยุด
def three_words(): yield "Python" yield "is" yield "expressive"
# Equivalent — for loop handles next() and StopIteration for youfor word in three_words(): print(word)generator expression
หัวข้อที่มีชื่อว่า “generator expression”เช่นเดียวกับที่ list comprehension สร้าง list generator expression ก็สร้าง generator โดยใช้วงเล็บแทนวงเล็บเหลี่ยม วิธีนี้ประหยัด memory เพราะไม่มีการสร้าง list ขึ้นมาจริง
squares_list = [x ** 2 for x in range(1_000_000)] # allocates a listsquares_gen = (x ** 2 for x in range(1_000_000)) # allocates almost nothing
# Consume lazilyfor sq in squares_gen: if sq > 25: breakgenerator expression เข้ากันได้อย่างเป็นธรรมชาติกับ built-in ที่รับ iterable:
total = sum(x ** 2 for x in range(10)) # no brackets needed inside sum()generator แบบไม่รู้จบ
หัวข้อที่มีชื่อว่า “generator แบบไม่รู้จบ”เนื่องจาก generator เป็นแบบ lazy เราจึงสามารถนิยามลำดับที่ไม่มีวันจบได้ ตราบเท่าที่ฝั่งผู้บริโภคหยุดร้องขอ
def fibonacci(): a, b = 0, 1 while True: # infinite loop — but never blocks yield a a, b = b, a + bเดโมที่รันได้แบบเต็ม
หัวข้อที่มีชื่อว่า “เดโมที่รันได้แบบเต็ม”from itertools import islice
def fibonacci(): """Infinite Fibonacci sequence generator.""" a, b = 0, 1 while True: yield a a, b = b, a + b
# Take only the first 10 values — safe because islice stops earlyfirst_10 = list(islice(fibonacci(), 10))print("First 10 Fibonacci numbers:")print(first_10)
# Generator expression: squares of even numberseven_squares = (x * x for x in range(1, 11) if x % 2 == 0)print("\nSquares of even numbers 1-10:")for val in even_squares: print(val, end=" ")print()
# Demonstrate single-use naturegen = (x * 3 for x in range(5))print("\nFirst pass:", list(gen))print("Second pass:", list(gen)) # exhausted — empty list
# Manual next() / StopIterationcounter = iter(range(3))print("\nManual iteration:")print(next(counter))print(next(counter))print(next(counter))try: print(next(counter))except StopIteration: print("StopIteration raised — generator exhausted")Loading Python runtime (first run only)…