เขียนไพธอนแบบนี้ ระวังโค้ดพัง MyPy ช่วยคุณได้จริงดิ
มาคุยกันเรื่องนึงที่หลายคนน่าจะเคยเจอครับ
คือเวลาเราเขียนโค้ด Python เนี่ย มันสะดวกนะ เพราะมันยืดหยุ่นมากๆ ไม่ต้องมานั่งประกาศ Type ให้วุ่นวายตอนแรกใช่มั้ยครับ
แต่...ความยืดหยุ่นนี่แหละ บางทีมันก็เป็นดาบสองคมนะ เคยไหมครับ โค้ดเขียนไปแล้ว ตอนรันจริง "อ้าวววว ทำไมพัง?!"
ส่วนใหญ่เลยนะ ที่เจอคือเรื่อง 'Type' นี่แหละครับ แบบว่า... เราส่งตัวเลขไปให้ฟังก์ชันที่คาดหวังข้อความ พอรันปุ๊บก็เละทันทีเลย บางทีกว่าจะรู้ก็ตอนโปรดักชันแล้วไง เศร้าเลย ใช่มั้ยล่ะ? ยิ่งโค้ดใหญ่ๆ คนทำหลายคนนี่ ปวดหัวเลยนะ
เออ วันนี้ผมเลยอยากชวนมาดูตัวช่วยตัวนึง ชื่อว่า MyPy ครับ มันเป็นเครื่องมือที่ช่วยเช็ค Type ของโค้ด Python เราได้ตั้งแต่ตอนเขียนเลย ไม่ต้องรอไปรันจริงถึงจะรู้ว่าผิดพลาดตรงไหน มันเป็นเหมือน 'ตาดี' ที่คอยบอกเราก่อนที่โค้ดจะไปถึงมือ user นะครับ
MyPy ทำอะไรได้บ้าง?
พูดง่ายๆ MyPy เนี่ย มันจะช่วยเรา 'ตรวจสอบ' ว่าเราใช้ Type ของข้อมูลในโค้ดได้ 'ถูกต้อง' ตามที่เราตั้งใจไว้รึเปล่า
มาลองดูกันดีกว่า ว่ามันทำงานยังไง?
ก่อนอื่นก็ต้องติดตั้งมันก่อนนะครับ ง่ายๆ เลย:
pip install mypy
ทีนี้ลองดูโค้ดที่ไม่มี Type Hint (อันนี้คือโค้ด Python ทั่วไปที่เราเขียนๆ กันนั่นแหละ):
def add_something(a, b):
return a + b
result = add_something(5, \"hello\") # ตรงนี้แหละ ตัวดีเลย! มันจะพังตอนรัน
print(result)
ถ้าเราเอาโค้ดนี้ไปรัน mypy your_file.py MyPy มันก็จะไม่ฟ้องอะไรมากหรอก เพราะเราไม่ได้บอกมันว่า a กับ b ควรเป็น Type อะไร ถูกไหม?
แต่ถ้าเราเริ่มใช้ Type Hint ล่ะ ทีนี้ MyPy จะฉลาดขึ้นเยอะเลย!
def add_numbers_correctly(a: int, b: int) -> int:
return a + b
# ลองส่งค่าผิด Type ให้มันดูนะ
problem_result = add_numbers_correctly(5, \"world\") # MyPy จะเจอตรงนี้แหละ!
# อันนี้ส่งถูกนะ MyPy ไม่ฟ้อง
ok_result = add_numbers_correctly(10, 20)
print(ok_result)
แล้วพอเราสั่ง mypy your_file.py อีกที สิ่งที่เราจะเห็นก็ประมาณนี้ครับ (อาจจะขึ้นชื่อไฟล์แล้วก็บรรทัดที่มันเจอ Error)
# Output จาก mypy ประมาณนี้เลย
your_file.py:6: error: Argument \"b\" to \"add_numbers_correctly\" has incompatible type \"str\"; expected \"int\" [arg-type]
Found 1 error in 1 file (checked 1 source file)
เห็นมะ? มันบอกเลยว่า b เนี่ย มันควรเป็น int ไม่ใช่ str มันช่วยเราประหยัดเวลา Debug ไปได้เยอะมากกกกกก เลยนะ
มาดูอีกตัวอย่างที่ซับซ้อนขึ้นอีกนิดหน่อยครับ การใช้ List หรือ Optionals
from typing import List, Optional
def process_items(items: List[str], count: Optional[int] = None) -> List[str]:
if count:
return items[:count]
return items
# MyPy โอเค
process_items([\"apple\", \"banana\"])
process_items([\"cat\", \"dog\"], 2)
# ลองให้ MyPy เจอข้อผิดพลาดดูนะ
# process_items([1, 2, 3]) # MyPy จะฟ้องว่า List ของคุณไม่ใช่ str
ประโยชน์ของการใช้ MyPy เนี่ยมีอะไรบ้าง?
- ลดบั๊ก: อันนี้แน่นอนครับ! เจอ Type Error ได้ตั้งแต่ก่อนรัน ก็แก้ได้เร็วขึ้นไง
- โค้ดอ่านง่ายขึ้น: พอมี Type Hint คนอื่นมาอ่านโค้ดเรา (หรือตัวเราเองในอนาคต) ก็จะเข้าใจว่าตัวแปรนี้มันควรเป็นข้อมูล Type ไหน
- ทำงานเป็นทีมง่ายขึ้น: พอทุกคนเข้าใจ Type ที่ชัดเจน การทำงานร่วมกันก็ผิดพลาดน้อยลงไง
- Refactoring สบายใจขึ้น: จะปรับโค้ดเยอะๆ ก็มั่นใจได้ว่า Type มันไม่หลุด
MyPy มันไม่ได้แก้ปัญหาได้ทุกอย่างนะ เพราะมันแค่เช็ค Type แต่มันก็ช่วยให้โค้ดเราแข็งแรงขึ้นเยอะเลย มันเหมือนมีเพื่อนมาช่วยเตือนตอนเขียนโค้ดน่ะครับ ลองไปใช้กันดูนะครับ แล้วจะรู้ว่ามันดีจริง!
อ้างอิง:
หวังว่าบทความนี้จะเป็นประโยชน์กับเพื่อนๆ โปรแกรมเมอร์ที่ cii3.net นะครับ ไว้เจอกันใหม่!