อิเทอเรเตอร์ (Iterators) เล่นบทบาทสำคัญใน Python พวกมันช่วยให้คุณสร้างออบเจ็กต์ที่สามารถวนซ้ำได้ นี่เป็นวิธีที่ดีในการจัดเก็บข้อมูลที่สามารถเข้าถึงได้ผ่านลูป for in
ปัญหาของอิเทอเร이터คือต้องเขียนโค้ดเยอะมากในการสร้าง มันมีประโยชน์แต่คุณต้องเขียนโค้ดมากจนเกินไปเพื่อให้มันทำงาน เจเนอเรเตอร์ (Generators) ช่วยแก้ปัญหานี้ได้
ในคู่มือนี้ เราจะพูดถึงว่า Python Generators คืออะไร และทำไมคุณควรใช้มัน เราจะนำ Generator มาใช้งานใน Python เพื่อช่วยให้คุณเข้าใจการทำงานของมัน
โดยไม่ล่าช้า มาเริ่มกันเลย!
Generator คืออะไร?
Generator เป็นวิธีง่ายๆ ในการสร้างอิเทอเรเตอร์ใน Python มันคือฟังก์ชันที่คืนค่าออบเจ็กต์ซึ่งคุณสามารถวนซ้ำได้
Generator บางครั้งเรียกว่า syntactic sugar (น้ำตาลเชิงไวยากรณ์) เพราะมันไม่ได้เพิ่มฟังก์ชันใหม่ๆ เข้าไปใน Python จริงๆ แต่มันช่วยทำให้โค้ดของคุณมีประสิทธิภาพมากขึ้น
เหมือนกับอิเทอเร이터 คุณสามารถวนซ้ำผ่าน Generator ได้เพียงครั้งเดียว เพราะ Generator ติดตามจำนวนครั้งที่ถูกวนซ้ำแล้ว และไม่สามารถรีเซ็ตได้
มาดูตัวอย่าง Python Iterator:
class Cookies:
def __init__(self, value):
self.value = value
def __iter__(self):
return self
def __next__(self):
return self.value
โค้ดนี้สร้างอิเทอเร이터ชื่อ Cookies เมื่อคุณส่งข้อมูลผ่านอิเทอเร이터นี้ มันจะถูกแปลงเป็นออบเจ็กต์ที่วนซ้ำได้ หมายความว่าคุณสามารถวนลูปผ่านข้อมูลโดยใช้ลูป for in หากคุณดูโค้ดนี้และคิดว่ามันยาวมาก นั่นเพราะมันยาวจริงๆ
Generator ช่วยให้เราลดขนาดโค้ดได้ มาเขียน Generator สำหรับอิเทอเร이터ข้างต้น:
def cookies(value):
while True:
yield value
โค้ดของเราลดขนาดลงมาก มาอธิบายว่ามันทำงานอย่างไร
Generator คือฟังก์ชันที่มีคีย์เวิร์ด yield แทนคำสั่ง return คำสั่ง Yield จะคืนค่าจากฟังก์ชัน
ความแตกต่างระหว่างคำสั่ง yield และ return คือ คำสั่ง return จะหยุดการทำงานของฟังก์ชัน ในขณะที่คำสั่ง yield จะหยุดชั่วคราวและต่อเนื่องในการวนซ้ำ
มาลองวนซ้ำ Generator function แบบง่ายของเรา:
for cookie in cookies("Raspberry"):
print(cookie)
โค้ดของเราคืนค่า:
Raspberry
Raspberry
…
โค้ดนี้จะทำซ้ำไปเรื่อยๆ จนกว่าเราจะหยุดโปรแกรม เพราะเราใช้ลูป while ที่ทำงานตลอดเวลา นี่ไม่ค่อยมีประโยชน์ในส่วนใหญ่ มาเขียน Generator ที่หยุดเมื่อทำการดำเนินการเสร็จสิ้น
วิธีเขียน Python Generator
เรามีลิสต์ของคุกกี้ที่ต้องการพิมพ์ออกคอนโซล ลิสต์นี้มีลักษณะดังนี้:
["Raspberry", "Choc-Chip", "Cinnamon", "Oat"]
เพื่อพิมพ์ออกคอนโซล เราสามารถสร้าง Generator แบบง่ายได้ เปิดไฟล์ Python ใหม่และวางโค้ดต่อไปนี้:
def print_cookies(cookies):
length = len(cookies)
for cookie in range(0, length):
yield cookies[cookie]
Generator ของเราจะวนผ่านทุกคุกกี้ในลิสต์ที่เราระบุ และคืนค่าแต่ละคุกกี้เป็นรายตัว Generator นี้ยังไม่ทำงาน เราต้องใช้ลูป for เพื่อวนซ้ำผ่านมัน:
cookie_list = ["Raspberry", "Choc-Chip", "Cinnamon", "Oat"]
for c in print_cookies(cookie_list):
print(c)
เรากำหนดอาร์เรย์ชื่อ cookie_list ที่เก็บลิสต์คุกกี้ 4 ชนิด เราจึงตั้งค่าลูป for ที่ใช้ Generator ของเราเพื่อวนผ่านค่าใน cookie_list
สำหรับแต่ละรอบในลูป for ออบเจ็กต์ที่สร้างขึ้นจะถูกพิมพ์ออกคอนโซล:
Raspberry
Choc-Chip
Cinnamon
Oat
เราทำได้แล้ว! Generator นี้คืนค่าจนกว่าจะวนครบทุกค่าในตัวแปร cookie_list
Generator มีกรณีการใช้งานหลากหลาย สมมติว่าเรามีลิสต์ราคาสั่งซื้อที่ต้องการรวมยอดรวม เราสามารถทำได้ด้วยโค้ดนี้:
def total_orders(orders):
length = len(orders)
total = 0
for o in range(0, length):
total += orders[o]
yield total
Generator นี้จะคำนวณยอดรวมสะสมของมูลค่าสั่งซื้อทั้งหมดในลิสต์ หลังจากวนซ้ำแต่ละรายการ Generator จะ yield ค่ายอดรวมปัจจุบันของออเดอร์ มาเขียนลูป for ที่ใช้ Generator ของเรา:
order_list = [2.30, 2.50, 1.95, 6.00, 7.50, 2.15]
for order in total_orders(order_list):
print(order)
โค้ดของเราคืนค่า:
2.3
4.8
6.75
12.75
20.25
22.4
Generator ของเราคำนวณมูลค่าสะสมของออเดอร์ทั้งหมดในตัวแปร order_list ทุกรอบการวนซ้ำจะคืนค่ามูลค่าสะสมใหม่
วิธีเขียน Generator Expression
Generator เขียนง่ายกว่าอิเทอเร이터แล้ว การ探索ของเราเพื่อเขียนโค้ดที่สะอาดกว่าไม่ต้องหยุดที่นี่ ขอบคุณ Generator Expressions
Generator Expressions คล้ายกับ List Comprehensions Generator Expressions สร้างรายการทีละรายการ เหมือน Generator นี่แตกต่างจาก List Comprehension ที่สร้างลิสต์ทั้งหมดในคราวเดียว
มาเขียน Generator สำหรับตัวอย่างคุกกี้ของเรา:
cookie_list = ["Raspberry", "Choc-Chip", "Cinnamon", "Oat"] cookie_generator = (cookie for cookie in cookie_list)
เรากำหนดลิสต์คุกกี้ในตัวแปร cookie_list เราจึงสร้าง Generator Expression นิพจน์นี้ใช้ไวยากรณ์เหมือน List Comprehensions แต่มีความแตกต่างใหญ่หนึ่งจุด: List Comprehensions ถูกกำหนดภายในวงเล็บเหลี่ยม ในขณะที่ Generator ถูกกำหนดภายในวงเล็บมุม
มาสร้างลูป for ที่วนซ้ำผ่านนิพจน์นี้:
for cookie in cookie_generator:
print(cookie)
โค้ดของเราคืนค่า:
Raspberry
Choc-Chip
Cinnamon
Oat
ผลตอบสนองของ Generator นี้เหมือนกับตัวอย่างแรกของเรา ไวยากรณ์ของเราชัดเจนมากขึ้น
สำคัญที่ต้องทราบว่าเมื่อคุณวนซ้ำ Generator ที่ประกาศด้วย Generator Expression คุณไม่ได้เรียกมันเป็นฟังก์ชัน Generator cookie_generator ของเราไม่รับค่าอินพุตใดๆ: มันมีโค้ดที่ต้องการเพื่อวนซ้ำผ่านลิสต์ cookie_list อยู่แล้ว
คุณควรใช้ไวยากรณ์ Generator Expressions เฉพาะเมื่อคุณต้องการเขียน Generator ที่ทำฟังก์ชันง่ายๆ การพิมพ์ลิสต์ค่าเป็นตัวอย่างที่ดี การคูณค่าในลิสต์ด้วยตัวเลขเฉพาะเป็นอีกตัวอย่างที่ดี
นี่เพราะในขณะที่ไวยากรณ์ Generator Expression ชัดเจน มันถูกออกแบบหลักสำหรับนิพจน์บรรทัดเดียว หาก Generator ของคุณจะใช้โค้ดมากกว่าหนึ่งบรรทัด ให้เขียนเป็น Generator Function โดยใช้ไวยากรณ์ที่เราพูดถึงก่อนหน้านี้ในบทความนี้
ทำไมถึงใช้ Generators?
เหตุผลหลักที่ใช้ Generator คือมันกระชับกว่าอิเทอเร이터
Generator คล้ายกับฟังก์ชันและไม่ต้องมีคำสั่ง __init__, __iter__, หรือ __next__ เพื่อทำงาน นี่แตกต่างจากอิเทอเร이터ที่ต้องการคำสั่งทั้งสาม นิสัยนี้หมายความว่าคุณสามารถนำ Generator มาใช้งานในจำนวนบรรทัดโค้ดน้อยลงอย่างมาก เมื่อเทียบกับการเขียนอิเทอเรเตอร์
เพิ่มเติม Generators มีประสิทธิภาพในการใช้หน่วยความจำ Generators สร้างรายการทีละรายการ พวกมันไม่สร้างลำดับทั้งหมดในหน่วยความจำก่อนคืนค่าผลลัพธ์ นี่ทำให้ Generators ใช้งานได้จริงหากคุณต้องการวนซ้ำผ่านลิสต์ข้อมูลขนาดใหญ่
วิธีการทำงานคือ อิเทอเรเตอร์ใช้วิธีการประเมินแบบขี้เกียจ (lazy evaluation) พวกมันสร้างองค์ประกอบถัดไปของออบเจ็กต์ที่วนซ้ำได้ก็ต่อเมื่อรายการนั้นถูกขอ
สรุป (และความท้าทาย)
Generator ช่วยให้คุณสร้างอิเทอเรเตอร์ที่เป็น Pythonic มากขึ้น
Generator ทำให้เขียนออบเจ็กต์โดยใช้โปรโตคอลอิเทอเรเตอร์ได้ง่ายขึ้น โดยไม่ต้องเขียนคำสั่ง __init__, __iter__, หรือ __next__ Generator ถูกกำหนดเป็นฟังก์ชัน ใช้คำสั่ง yield เพื่อหยุด Generator และส่งค่ากลับไปยังโปรแกรมหลักก่อนกลับมาทำงานต่อ
หากคุณมองหาความท้าทาย นี่คือบางไอเดีย:
- เขียน Generator ที่กลับด้านสตริง
- เขียน Generator ที่คืนค่าเฉพาะค่าที่มีคำเฉพาะจากลิสต์ค่า
- เขียน Generator ที่คูณทุกตัวเลขในลิสต์ด้วยสอง
สำหรับการอ่านเพิ่มเติม ดูบทช่วยสอนของเราเกี่ยวกับวิธีเขียนและใช้ Python Iterators ตอนนี้คุณพร้อมที่จะเริ่มทำงานกับ Generators ใน Python อย่างมืออาชีพแล้ว!