วันอาทิตย์ที่ 6 เมษายน พ.ศ. 2568

Python Type Hinting

 หลังจากเรียน PEP8 มาแล้ว หัวข้อต่อมาเป็น Type Hinting


Type Hinting คืออะไร?

    Type hinting คือการระบุประเภทของตัวแปรที่ใช้ในฟังก์ชันหรือคลาสใน Python เพื่อช่วยให้โค้ดอ่านง่ายขึ้น และช่วยลดข้อผิดพลาดที่อาจเกิดจากการใช้ตัวแปรผิดประเภท


    ใน python ไม่ได้บังคับให้ใส่ type hinting คือโปรแกรมก็ยังทำงานได้ปกติแม้จะไม่มี type hinting

ใน Python เริ่มรองรับอย่างเป็นทางการตั้งแต่ Python 3.5 เป็นต้นไป ผ่านมาตรฐานใน PEP 484


Type Hinting ช่วยทำให้โค้ดอ่านง่ายขึ้นและลดข้อผิดพลาดอย่างไร?

  1. ช่วยให้โค้ดอ่านง่ายขึ้น

    • คนอื่นที่อ่านโค้ดจะเข้าใจได้ง่ายขึ้นว่าแต่ละตัวแปรควรเป็นประเภทไหน

    • ลดความสับสนโดยเฉพาะเมื่อโค้ดมีความซับซ้อน

  2. ลดข้อผิดพลาดจากการใช้ตัวแปรผิดประเภท

    • ตัวแปรที่ใช้ผิดประเภทอาจทำให้เกิดข้อผิดพลาดในระหว่างรันไทม์ (Runtime error)

  3. ช่วยให้ออโต้คอมพลีตและเครื่องมือพัฒนาโค้ดทำงานได้ดีขึ้น

    • IDE อย่าง PyCharm หรือ VS Code สามารถให้คำแนะนำและตรวจสอบโค้ดอัตโนมัติได้ดีขึ้นเมื่อมี Type Hinting


    ถ้าให้อธิบายง่ายๆก็คือ เหมือน comment บอกหน่อยว่าตัวแปรนี้ใช้ type ไหนบ้าง ถ้ากลับมาอ่านก็จะได้นึกออกง่ายขึ้น แต่ที่ไม่เขียนเป็น comment เพราะ มี format เฉพาะให้ IDE มาอ่านกับเราด้วย มีเครื่องมือช่วยตรวจสอบด้วย (mypy)

    ที่บอกว่าเหมือน comment เพราะว่า ต่อให้ใส่ไปแล้ว แต่เราใช้ผิด type จากที่เขียนไว้ โปรแกรมเราก็ยังคงทำงานได้ปกติ ไม่มี error อะไร

การเขียน type hinting แบบต่างๆ มีดังนี้

 1. การใช้ Type Hinting กับตัวแปร (Variable Annotation)

Python อนุญาตให้กำหนดชนิดของตัวแปรโดยใช้ : ตามด้วยชนิดของข้อมูล


x: int = 10 y: float = 3.14 name: str = "Alice" is_active: bool = True

📌 ข้อดี

  • ช่วยให้คนอ่านโค้ดเข้าใจได้ทันทีว่าตัวแปรนี้ควรเก็บค่าอะไร

  • IDE สามารถให้คำแนะนำที่แม่นยำขึ้น


🔹 2. การใช้ Type Hinting กับฟังก์ชัน (Function Annotations)

สามารถกำหนดชนิดของพารามิเตอร์และค่าที่ฟังก์ชันคืนค่าได้


def add(x: int, y: int) -> int: return x + y

📌 โค้ดด้านบนหมายความว่า

  • x และ y ต้องเป็น int

  • ฟังก์ชันต้องคืนค่าเป็น int

🔹 รองรับหลายชนิดของข้อมูล (Multiple Types)


from typing import Union def process(value: Union[int, float]) -> float: return value * 2.5 print(process(10)) # 25.0 print(process(3.2)) # 8.0
  • ใช้ Union กำหนดให้พารามิเตอร์รับได้มากกว่าหนึ่งชนิด

หรือหากใช้งาน python 3.10 ขึ้นไป สามารถใช้ | คั่นระหว่าง type ได้

from typing import Union def process(value: int | float) -> float: return value * 2.5 print(process(10)) # 25.0 print(process(3.2)) # 8.0



🔹 3. การใช้ Optional สำหรับค่าที่อาจเป็น None

บางฟังก์ชันอาจต้องรับค่าหรือคืนค่า None ได้ เราใช้ Optional (จริง ๆ ก็คือ Union[type, None])


from typing import Optional def get_username(user_id: int) -> Optional[str]: users = {1: "Alice", 2: "Bob"} return users.get(user_id) # อาจคืนค่า str หรือ None print(get_username(1)) # "Alice" print(get_username(3)) # None

📌 ควรใช้ Optional เมื่อค่าที่คืนกลับอาจเป็น None

🔹 4. การใช้ Type Hinting กับ List, Tuple, Set, Dict

หากต้องการระบุชนิดของข้อมูลในโครงสร้างข้อมูลหลายตัว เราใช้ List[type], Dict[key, value] เป็นต้น


from typing import List, Tuple, Set, Dict numbers: List[int] = [1, 2, 3, 4] coordinates: Tuple[float, float] = (13.5, 100.7) unique_values: Set[str] = {"apple", "banana", "cherry"} user_info: Dict[str, int] = {"Alice": 25, "Bob": 30}

📌 โครงสร้างข้อมูลหลัก ๆ ที่ใช้บ่อย

  • List[type] → ใช้กับลิสต์ของค่าชนิดเดียวกัน

  • Tuple[type, ...] → ใช้กับทูเพิลที่มีขนาดแน่นอน

  • Set[type] → ใช้กับเซ็ตที่มีค่าชนิดเดียวกัน

  • Dict[key_type, value_type] → ใช้กับดิกชันนารีที่มีคีย์และค่าที่กำหนดชนิดได้


🔹 5. การใช้ Any เมื่อไม่ต้องการกำหนดชนิดแน่นอน

หากตัวแปรสามารถเป็นอะไรก็ได้ สามารถใช้ Any


from typing import Any def process(value: Any) -> Any: return value x: Any = 42 y: Any = "Hello"

📌 ข้อเสียของ Any

  • ทำให้ Type Checking ไร้ประโยชน์

  • ควรใช้เมื่อจำเป็นเท่านั้น เช่น ฟังก์ชันที่ต้องรองรับทุกประเภทข้อมูล

ตรงนี้คือถ้าใส่ any ก็เหมือนเราไม่ได้เขียน type hinting แหละ คิดว่าน่าจะมีเผื่อไว้ในบางกรณี

🔹 6. การใช้ Callable สำหรับฟังก์ชันเป็นพารามิเตอร์

หากต้องการรับฟังก์ชันเป็นอาร์กิวเมนต์ สามารถใช้ Callable


from typing import Callable def apply_function(x: int, func: Callable[[int], int]) -> int: return func(x) def square(n: int) -> int: return n * n print(apply_function(5, square)) # 25

📌 Callable[[พารามิเตอร์], ReturnType]

  • Callable[[int], int] → รับ int คืนค่า int


🔹 7. การใช้ TypeVar สำหรับ Generic Type

หากต้องการให้ฟังก์ชันรองรับได้หลายชนิดของข้อมูล ใช้ TypeVar


from typing import TypeVar T = TypeVar('T') def identity(value: T) -> T: return value print(identity(10)) # 10 print(identity("Hello")) # "Hello"

📌 TypeVar เหมาะสำหรับการสร้างโค้ด Generic ที่รองรับหลายชนิดข้อมูล


🔹 8. การใช้ TypedDict สำหรับดิกชันนารีที่มีโครงสร้างแน่นอน

ใช้ TypedDict กำหนดดิกชันนารีที่มีคีย์และชนิดข้อมูลที่แน่นอน


from typing import TypedDict class User(TypedDict): name: str age: int user: User = {"name": "Alice", "age": 30}

📌 เหมาะกับการกำหนดโครงสร้างของ JSON หรือข้อมูลที่แน่นอน


🔹 9. การใช้ Literal สำหรับค่าที่แน่นอน

ใช้ Literal กำหนดว่าตัวแปรสามารถมีค่าอะไรได้บ้าง


from typing import Literal def set_status(status: Literal["active", "inactive"]) -> str: return f"Status set to {status}" print(set_status("active")) # ✅ ถูกต้อง # print(set_status("unknown")) # ❌ mypy จะเตือนว่าไม่ถูกต้อง

📌 **เหมาะสำหรับค่าที่เป็นตัวเลือกคงที่ เช่น "GET" | "POST" | "PUT"


สำหรับหัวข้อนี้ ผมยังไม่ค่อยเข้าใน TypeVar เท่าไหร่ ไว้อ่านเพิ่มจนเข้าใจแล้วจะมาอธิบายเพิ่มนะครับ

ช่วงนี้ก็ใช้ตัวอื่นไปก่อน

ไม่มีความคิดเห็น:

แสดงความคิดเห็น