Custom Errors
ทำไม custom exception จึงสำคัญ
หัวข้อที่มีชื่อว่า “ทำไม custom exception จึงสำคัญ”Exception built-in ของ Python (ValueError, TypeError, RuntimeError, …) มีความหมายทั่วไปมาก
เมื่อโค้ด raise ValueError caller ไม่รู้ว่ามาจาก input ของผู้ใช้, ไฟล์ config ที่เสีย, หรือ network response
Custom exception class ให้ error ของคุณมีประเภทที่ชัดเจน ให้ caller จับได้ตามต้องการ, เพิ่ม attribute ที่มีโครงสร้าง (เช่น HTTP status code หรือชื่อ field), และบอกได้ว่าแต่ละส่วนของแอปพลิเคชันอาจเกิดอะไรขึ้น
การ subclass Exception
หัวข้อที่มีชื่อว่า “การ subclass Exception”Custom exception ขั้นต่ำคือ class ที่สืบทอดจาก Exception:
class AppError(Exception): """Base class for all application errors."""แค่นี้ก็พอสำหรับ raise และ catch ตามชื่อได้:
raise AppError("something went wrong")การเพิ่ม custom attribute
หัวข้อที่มีชื่อว่า “การเพิ่ม custom attribute”Override __init__ เพื่อแนบข้อมูลเพิ่มเติมกับ exception:
class AppError(Exception): def __init__(self, message: str, code: int = 0) -> None: super().__init__(message) # ส่ง message ให้ Exception self.code = codeCaller สามารถอ่าน err.code เพื่อรับข้อมูลที่มีโครงสร้างโดยไม่ต้อง parse string ข้อความ
การสร้าง hierarchy
หัวข้อที่มีชื่อว่า “การสร้าง hierarchy”สร้าง subclass สำหรับแต่ละหมวดหมู่ความผิดพลาด ทุก NotFoundError ก็เป็น AppError ด้วย ดังนั้น caller จับได้ที่ระดับความเฉพาะเจาะจงที่ต้องการ
class ValidationError(AppError): def __init__(self, field: str, message: str) -> None: super().__init__(f'Validation failed on "{field}": {message}', code=400) self.field = field
class NotFoundError(AppError): def __init__(self, resource: str, resource_id: int) -> None: super().__init__(f"{resource} id={resource_id} not found", code=404) self.resource = resource self.resource_id = resource_idการจับที่ระดับต่างกัน:
try: raise ValidationError("email", "must contain @")except ValidationError as e: print(f"Field {e.field!r} invalid: {e}") # เฉพาะเจาะจงexcept AppError as e: print(f"App error [{e.code}]: {e}") # กว้างกว่าstr(exception) และ repr(exception)
หัวข้อที่มีชื่อว่า “str(exception) และ repr(exception)”str(e) คืน message ที่ส่งให้ super().__init__() นี่คือสิ่งที่แสดงเมื่อ print(e) หรือใช้ใน f-string
repr(e) คืน ExceptionClass(message) — ใช้สำหรับ logging
Demo แบบ runnable เต็มรูปแบบ
หัวข้อที่มีชื่อว่า “Demo แบบ runnable เต็มรูปแบบ”# --- base application error ---class AppError(Exception): """Base class for all application errors.""" def __init__(self, message: str, code: int = 0) -> None: super().__init__(message) self.code = code
# --- specific subclasses ---class ValidationError(AppError): """Raised when input validation fails.""" def __init__(self, field: str, message: str) -> None: super().__init__( f'Validation failed on field "{field}": {message}', code=400, ) self.field = field
class NotFoundError(AppError): """Raised when a requested resource does not exist.""" def __init__(self, resource: str, resource_id: int) -> None: super().__init__( f"{resource} with id={resource_id} not found", code=404, ) self.resource = resource self.resource_id = resource_id
# --- catching at different levels ---errors = [ ValidationError("email", "must contain @"), NotFoundError("User", 42), AppError("generic app failure", code=500),]
for err in errors: try: raise err except ValidationError as e: print(f"[{e.code}] Validation on {e.field!r}: {e}") except NotFoundError as e: print(f"[{e.code}] {e.resource} not found (id={e.resource_id})") except AppError as e: print(f"[{e.code}] AppError: {e}")
# --- isinstance hierarchy ---print()e = ValidationError("name", "too short")print(f"ValidationError? {isinstance(e, ValidationError)}")print(f"AppError? {isinstance(e, AppError)}")print(f"Exception? {isinstance(e, Exception)}")print(f"BaseException? {isinstance(e, BaseException)}")Loading Python runtime (first run only)…