Skip to content

บทที่ 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 โดยไม่โหลดเนื้อหาทั้งหมดลงในหน่วยความจำ

def read_large_file(file_path):
    with open(file_path, "r", encoding="utf-8") as file:
        for line in file:
            yield line.strip()

# เรียกใช้งานอ่านทีละบรรทัดโดยไม่กิน RAM
# for line in read_large_file("huge_log.txt"):
#     if "ERROR" in line:
#         print(line)