Python การใช้งาน Iterator

Iterator ใน Python เป็นวัตถุ (object) ที่อนุญาตให้เราทำการวนซ้ำ (iterate) ข้อมูล ซึ่งช่วยให้เราสามารถเข้าถึงข้อมูลภายในวัตถุนั้นทีละตัวได้โดยไม่จำเป็นต้องโหลดข้อมูลทั้งหมดในหน่วยความจำพร้อมกัน

คุณสมบัติของ Iterator

ใน Python สิ่งที่สามารถเป็น iterator ได้จะต้องมีสองอย่างคือ

  1. มีเมธอด __iter__() ซึ่งจะคืนค่าตัว iterator ของตัวเอง
  2. มีเมธอด __next__() ซึ่งคืนค่าข้อมูลตัวถัดไป ถ้าไม่มีข้อมูลให้คืนค่าแล้วจะต้องโยนข้อยกเว้น StopIteration

การสร้าง Iterator ใน Python

การใช้งาน iterator จาก object ที่เป็น iterable

Python
# ตัวอย่างที่ใช้ list (ซึ่งเป็น iterable object)
my_list = [1, 2, 3, 4]
my_iter = iter(my_list)

print(next(my_iter))  # ผลลัพธ์: 1
print(next(my_iter))  # ผลลัพธ์: 2

ในตัวอย่างนี้ my_list เป็น iterable (เช่น list, tuple หรือ string) และ iter(my_list) จะสร้าง iterator จากมัน เมธอด next() จะคืนค่าข้อมูลถัดไปใน iterable

การสร้าง class ของตัวเองเพื่อเป็น Iterator

Python
class MyNumbers:
    def __iter__(self):
        self.current = 1
        return self
    
    def __next__(self):
        if self.current <= 5:
            num = self.current
            self.current += 1
            return num
        else:
            raise StopIteration

# สร้าง iterator จาก class ที่สร้างเอง
my_numbers = MyNumbers()
my_iter = iter(my_numbers)

for num in my_iter:
    print(num)

ในตัวอย่างนี้ เราสร้าง class MyNumbers ที่กำหนดให้ตัวเองเป็น iterator โดยการมีเมธอด __iter__() และ __next__() เมธอด __next__() จะคืนค่าเลขถัดไปจนถึง 5 แล้วจึงโยนข้อยกเว้น StopIteration เพื่อหยุดการวนซ้ำ

การใช้งาน Generator (เป็นรูปแบบหนึ่งของ iterator)

Generator เป็นฟังก์ชันพิเศษที่ใช้ yield เพื่อคืนค่าครั้งละหนึ่งค่าทุกครั้งที่เรียก next() และจะจำสถานะการทำงานไว้จนกว่าจะถูกเรียกใช้งานอีกครั้ง

Python
def my_generator():
    for i in range(1, 6):
        yield i

gen = my_generator()

for value in gen:
    print(value)

ในตัวอย่างนี้ ฟังก์ชัน my_generator จะคืนค่าแต่ละตัวในช่วง 1 ถึง 5 ด้วยคำสั่ง yield และสามารถใช้ในลูปได้โดยตรง

การประยุกต์ใช้งาน Iterator

การอ่านไฟล์ทีละบรรทัด เมื่อไฟล์ใหญ่เกินไป การโหลดไฟล์ทั้งหมดเข้าในหน่วยความจำอาจไม่เหมาะสม Iterator ช่วยให้เราสามารถอ่านไฟล์ทีละบรรทัด

Python
with open('example.txt', 'r') as file:
    for line in iter(file.readline, ''):
        print(line.strip())

การสร้างลำดับข้อมูลแบบไม่สิ้นสุด สามารถใช้ iterator ในการสร้างลำดับข้อมูลขนาดใหญ่หรือไม่มีที่สิ้นสุด เช่น ลำดับ Fibonacci

Python
class Fibonacci:
    def __iter__(self):
        self.a, self.b = 0, 1
        return self
    
    def __next__(self):
        fib = self.a
        self.a, self.b = self.b, self.a + self.b
        return fib

fib_seq = Fibonacci()
fib_iter = iter(fib_seq)

for _ in range(10):
    print(next(fib_iter))

Looping Through an Iterator

การ Looping Through an Iterator เป็นวิธีการใช้ iterator เพื่อวนซ้ำ (loop) ผ่านค่าที่อยู่ในวัตถุ (object) ทีละตัวอย่างมีประสิทธิภาพ โดยไม่จำเป็นต้องโหลดข้อมูลทั้งหมดเข้าในหน่วยความจำในครั้งเดียว

การทำงานเบื้องหลังของการ Looping Through an Iterator

  • เมื่อเรานำ iterator มาใช้กับลูป (เช่น for loop หรือ while loop) Python จะทำงานดังนี้
    • Python จะเรียกใช้เมธอด __iter__() เพื่อรับตัว iterator ของ object นั้น
    • จากนั้นจะเรียกใช้เมธอด __next__() ของ iterator เพื่อรับค่าถัดไปในลำดับ
    • เมื่อถึงจุดที่ไม่มีค่าที่จะคืนอีก เมธอด __next__() จะโยนข้อยกเว้น StopIteration ซึ่งเป็นสัญญาณให้ลูปหยุดการทำงาน

การใช้งาน Iterator กับ For Loop

for loop เป็นวิธีการทั่วไปที่สุดในการวนซ้ำผ่าน iterator ตัวอย่างเช่น

Python
# ตัวอย่างการใช้ for loop กับ iterator
my_list = [10, 20, 30]
my_iter = iter(my_list)

for value in my_iter:
    print(value)

ในตัวอย่างนี้ Python จะใช้เมธอด __next__() ภายในลูปเพื่อดึงค่าจาก my_iter ทีละค่า และพิมพ์ออกมา

ผลลัพธ์

การใช้งาน Iterator กับ While Loop

ในบางกรณี เราสามารถใช้ while loop เพื่อควบคุมการวนซ้ำผ่าน iterator ได้ด้วยการใช้ next() โดยตรงและจัดการ StopIteration ด้วย try-except เช่น

Python
my_list = [1, 2, 3]
my_iter = iter(my_list)

while True:
    try:
        value = next(my_iter)
        print(value)
    except StopIteration:
        break

ในตัวอย่างนี้ while loop จะทำงานจนกว่า StopIteration จะเกิดขึ้น และใช้ try-except เพื่อจับข้อยกเว้นและหยุดลูปเมื่อ iterator ไม่มีค่าเหลือให้ดึงอีก

ผลลัพธ์

การทำงานของ Iterator ภายใน For Loop

เบื้องหลังของ for loop Python จะทำสิ่งต่อไปนี้

  1. สร้าง iterator ด้วยการเรียก iter()
  2. เรียก next() ภายในลูปเพื่อดึงค่าจาก iterator
  3. จับ StopIteration เพื่อหยุดลูป

ตัวอย่างต่อไปนี้เป็นตัวอย่างการจำลองการทำงานภายใน for loop แบบเต็ม

Python
my_list = [1, 2, 3]
my_iter = iter(my_list)

# จำลองการทำงานของ for loop
while True:
    try:
        value = next(my_iter)
        print(value)
    except StopIteration:
        break

ผลลัพธ์ที่ได้จะเหมือนกับการใช้ for loop โดยตรง

การใช้งานอื่น ๆ ในการวนซ้ำผ่าน Iterator

การประยุกต์ใช้กับไฟล์

เมื่อทำการอ่านไฟล์ทีละบรรทัด ไฟล์ใน Python สามารถถูกมองว่าเป็น iterator โดยสามารถวนซ้ำผ่านไฟล์ได้อย่างสะดวก

Python
with open('example.txt', 'r') as file:
    for line in file:
        print(line.strip())

การใช้งาน Generator กับ For Loop

Generator เป็นอีกหนึ่งรูปแบบของ iterator ที่ใช้สำหรับการสร้างลำดับข้อมูลที่มีขนาดใหญ่มากหรือไม่จำกัด ซึ่งทำงานร่วมกับ for loop ได้อย่างดี

Python
def my_generator():
    for i in range(1, 4):
        yield i

gen = my_generator()

for value in gen:
    print(value)

ผลลัพธ์

StopIteration

StopIteration เป็นข้อยกเว้น (exception) ใน Python ที่ถูกใช้โดย iterator เพื่อส่งสัญญาณบอกว่าไม่มีข้อมูลเพิ่มเติมให้ดึงออกมาแล้ว ซึ่งมักจะเกิดขึ้นเมื่อ iterator ถูกเรียกใช้จนหมดข้อมูล เช่น ในการเรียกใช้เมธอด __next__() ของ iterator จนครบทุกค่า

ลักษณะการทำงานของ StopIteration

  1. เมื่อเราสร้าง iterator จากวัตถุที่เป็น iterable เช่น list, tuple, หรือ string จะมีการดึงค่าผ่านเมธอด __next__() ซึ่งดึงข้อมูลทีละตัวจนกว่าจะไม่มีข้อมูลเหลือ
  2. เมื่อ iterator ถูกใช้งานจนหมดข้อมูลแล้ว การเรียก __next__() ครั้งถัดไปจะโยนข้อยกเว้น StopIteration ออกมาเพื่อบอกให้หยุดการวนซ้ำ

ตัวอย่างการใช้งาน StopIteration ใน Iterator

ตัวอย่างง่าย ๆ การทำงานของ StopIteration ใน Python มีดังนี้

Python
my_list = [1, 2, 3]
my_iter = iter(my_list)

# ใช้ next() เพื่อดึงค่าทีละตัว
print(next(my_iter))  # ผลลัพธ์: 1
print(next(my_iter))  # ผลลัพธ์: 2
print(next(my_iter))  # ผลลัพธ์: 3

# ถ้าเรียก next() อีกครั้งเมื่อไม่มีข้อมูล จะเกิด StopIteration
try:
    print(next(my_iter))
except StopIteration:
    print("No more items.")

ในตัวอย่างนี้ เราสร้าง my_iter จาก list ที่มี 3 ค่า การเรียก next() 3 ครั้งแรกจะดึงค่ามาได้ตามลำดับ แต่เมื่อเรียกครั้งที่ 4 จะเกิด StopIteration เพราะไม่มีค่าเหลือใน iterator

ผลลัพธ์

การใช้งาน StopIteration กับ for loop

เมื่อใช้ for loop กับ iterable object เช่น list หรือ generator Python จะจัดการข้อยกเว้น StopIteration ให้โดยอัตโนมัติ เราจึงไม่จำเป็นต้องใช้ try-except เอง

ตัวอย่าง

Python
my_list = [1, 2, 3]

# for loop จะจัดการ StopIteration ให้อัตโนมัติ
for item in my_list:
    print(item)

ในเบื้องหลัง Python จะเรียกใช้ next() กับ iterator และหยุดลูปเมื่อเกิด StopIteration โดยที่เราไม่ต้องเขียนโค้ดจัดการข้อยกเว้นนี้ด้วยตัวเอง

ผลลัพธ์

การใช้ StopIteration ใน Generator

ในฟังก์ชัน generator ค่าจะถูกส่งคืนด้วย yield จนกว่าจะไม่มีค่าเพิ่มเติม ในกรณีนี้ Python จะโยนข้อยกเว้น StopIteration โดยอัตโนมัติเมื่อ generator หมดค่าที่จะส่งคืน

Python
def my_generator():
    yield 1
    yield 2
    yield 3

gen = my_generator()

for value in gen:
    print(value)

เมื่อ generator ส่งคืนค่าครบแล้วจะหยุดทำงานโดยไม่ต้องจัดการ StopIteration ด้วยตัวเอง Python จะจัดการให้ภายใน for loop

ผลลัพธ์

แชร์เรื่องนี้