Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

วิธีเขียน Python Generator

อิเทอเรเตอร์ (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 อย่างมืออาชีพแล้ว!