บทที่ 5.2: Iterators และ Generators (การใช้งาน yield เพื่อประหยัดหน่วยความจำ)
เอกสารนี้อธิบายเกี่ยวกับความแตกต่างระหว่าง Iterable และ Iterator การสร้าง Generator ด้วยคำสั่ง yield การประหยัดพื้นที่หน่วยความจำ (Memory Efficiency) และการใช้งาน Generator Expressions
1. แนวคิดพื้นฐาน: Iterable และ Iterator
- Iterable: Object ที่สามารถนำไปวนซ้ำได้ เช่น List, Tuple, Dict, String โดยตัวมันเองจะมี Dunder Method
__iter__() - Iterator: Object ที่ทำหน้าที่ดึงข้อมูลทีละตัวผ่าน Dunder Method
__next__()เมื่อดึงข้อมูลจนหมดจะส่งข้อผิดพลาดStopIteration
numbers = [10, 20, 30]
# แปลง Iterable เป็น Iterator
my_iterator = iter(numbers)
# ดึงข้อมูลทีละตัวด้วย next()
print(next(my_iterator)) # Output: 10
print(next(my_iterator)) # Output: 20
print(next(my_iterator)) # Output: 30
# print(next(my_iterator)) # เกิด StopIteration
2. Generators และคำสั่ง yield
Generator คือฟังก์ชันพิเศษที่สร้าง Iterator ขึ้นมาโดยอัตโนมัติ โดยแทนที่จะใช้ return คืนค่าทั้งหมดพร้อมกัน จะใช้ yield เพื่อคืนค่าทีละตัวและหยุดการทำงานชั่วคราว (Pause) รอการเรียกใช้ครั้งถัดไป
2.1 ความแตกต่างระหว่าง return และ yield
return: คืนค่าผลลัพธ์ทันทีและจบการทำงานของฟังก์ชันyield: คืนค่าผลลัพธ์ ณ บรรทัดนั้น แล้วจำสภาวะการทำงาน (State) ไว้ เมื่อถูกเรียกใช้ถัดไปจะทำงานต่อจากบรรทัดเดิม
# ฟังก์ชันทั่วไป
def get_numbers_list(n):
result = []
for i in range(n):
result.append(i)
return result
# Generator Function
def get_numbers_generator(n):
for i in range(n):
yield i
# การใช้งาน Generator
gen = get_numbers_generator(3)
print(next(gen)) # Output: 0
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
3. การประหยัดหน่วยความจำ (Memory Efficiency)
การใช้ Generator จะสร้างข้อมูลทีละตัวเมื่อถูกเรียกใช้งาน (Lazy Evaluation) ต่างจาก List ที่จะต้องสร้างข้อมูลทั้งหมดเก็บไว้ในหน่วยความจำ (RAM) ทันที
import sys
# List Comprehension (สร้างข้อมูล 1,000,000 ตัวเก็บใน RAM)
list_data = [i for i in range(1000000)]
# Generator Expression (สร้างข้อมูลทีละตัวเมื่อต้องการใช้)
gen_data = (i for i in range(1000000))
print(f"ขนาดของ List: {sys.getsizeof(list_data):,} bytes")
print(f"ขนาดของ Generator: {sys.getsizeof(gen_data):,} bytes")
# Output:
# ขนาดของ List: 8,448,728 bytes (โดยประมาณ)
# ขนาดของ Generator: 208 bytes (โดยประมาณ)
4. ตัวอย่างการประยุกต์ใช้งานจริง (Reading Large Files)
การอ่านไฟล์ขนาดใหญ่ระดับ Gigabytes โดยไม่โหลดเนื้อหาทั้งหมดลงในหน่วยความจำ