บทที่ 6.1: Exception Handling (การจัดการ Error ด้วย try-except-finally)
เอกสารนี้อธิบายถึงกลไกการจัดการข้อผิดพลาด (Exception Handling) ในภาษา Python เพื่อป้องกันไม่ให้โปรแกรมหยุดทำงานกะทันหันเมื่อเกิด Error ในขณะ Runtime รวมถึงการสร้าง Custom Exception และการจัดการทรัพยากรด้วยบล็อก try-except-else-finally
1. โครงสร้าง try-except-else-finally
การจัดการ Exception ใน Python ใช้โครงสร้าง 4 ส่วนหลัก โดยแต่ละส่วนมีหน้าที่เฉพาะดังนี้:
try: ใส่โค้ดที่มีความเสี่ยงว่าจะเกิดข้อผิดพลาดexcept: ใส่โค้ดที่จะทำงานเมื่อเกิดข้อผิดพลาดที่ระบุไว้else: ใส่โค้ดที่จะทำงาน เมื่อไม่มีข้อผิดพลาดเกิดขึ้นเลย ในบล็อกtryfinally: ใส่โค้ดที่จะ ทำงานเสมอ ไม่ว่าจะเกิดข้อผิดพลาดขึ้นหรือไม่ก็ตาม
def divide_numbers(a, b):
try:
result = a / b
except ZeroDivisionError:
print("ข้อผิดพลาด: ไม่สามารถหารด้วยศูนย์ได้")
else:
print(f"การคำนวณสำเร็จ: {result}")
finally:
print("จบกระบวนการคำนวณ\n")
divide_numbers(10, 2)
# Output:
# การคำนวณสำเร็จ: 5.0
# จบกระบวนการคำนวณ
divide_numbers(10, 0)
# Output:
# ข้อผิดพลาด: ไม่สามารถหารด้วยศูนย์ได้
# จบกระบวนการคำนวณ
2. การดักจับ Exception หลายรูปแบบ (Multiple Exceptions)
เราสามารถดักจับข้อผิดพลาดหลายชนิดได้โดยแยกบล็อก except ตามประเภทของ Exception หรือรวมไว้ใน Tuple เดียวกัน
def parse_and_get_element(data_list, index_str):
try:
index = int(index_str)
value = data_list[index]
print(f"ข้อมูลตำแหน่งที่ {index}: {value}")
except ValueError:
print("ข้อผิดพลาด: ข้อมูลอินพุตไม่ใช่ตัวเลขจำนวนเต็ม")
except IndexError:
print("ข้อผิดพลาด: ตำแหน่ง Index เกินขนาดของ List")
except Exception as e:
# ดักจับข้อผิดพลาดทั่วไปที่ไม่ได้ระบุไว้ข้างต้น
print(f"เกิดข้อผิดพลาดไม่ทราบสาเหตุ: {type(e).__name__} - {e}")
data = ["A", "B", "C"]
parse_and_get_element(data, "1") # Output: ข้อมูลตำแหน่งที่ 1: B
parse_and_get_element(data, "abc") # Output: ข้อผิดพลาด: ข้อมูลอินพุตไม่ใช่ตัวเลขจำนวนเต็ม
parse_and_get_element(data, "5") # Output: ข้อผิดพลาด: ตำแหน่ง Index เกินขนาดของ List
3. การโยนข้อผิดพลาดและการสร้าง Custom Exception
3.1 การส่งผ่านข้อผิดพลาดด้วย raise
สามารถใช้คำสั่ง raise เพื่อสั่งให้เกิด Exception ขึ้นตามเงื่อนไขที่ต้องการได้
def set_age(age: int):
if age < 0:
raise ValueError("อายุไม่สามารถติดลบได้")
print(f"ตั้งค่าอายุเป็น {age} เรียบร้อย")
try:
set_age(-5)
except ValueError as e:
print(f"การทำงานล้มเหลว: {e}")
# Output: การทำงานล้มเหลว: อายุไม่สามารถติดลบได้
3.2 การสร้าง Custom Exception Class
สร้าง Class ข้อผิดพลาดขึ้นเองโดยการสืบทอดจากคลาส Exception หลัก
class InsufficientFundsError(Exception):
"""Exception เมื่อยอดเงินในบัญชีไม่พอสำหรับถอน"""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(f"ยอดเงินคงเหลือ ({balance}) ไม่เพียงพอสำหรับถอน ({amount})")
class BankAccount:
def __init__(self, balance):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
return self.balance
account = BankAccount(1000)
try:
account.withdraw(1500)
except InsufficientFundsError as e:
print(f"เกิดข้อผิดพลาด: {e}")
print(f"ยอดเงินคงเหลือปัจจุบัน: {e.balance}")
# Output:
# เกิดข้อผิดพลาด: ยอดเงินคงเหลือ (1000) ไม่เพียงพอสำหรับถอน (1500)
# ยอดเงินคงเหลือปัจจุบัน: 1000
4. Exception Chaining (raise ... from ...)
เมื่อต้องการดักจับข้อผิดพลาดหนึ่งแล้วครอบด้วยข้อผิดพลาดชนิดใหม่ สามารถใช้ from เพื่อรักษา Context ของข้อผิดพลาดเดิมเอาไว้ใน Traceback ได้