On this page
Exception เจาะลึก
จัดการข้อผิดพลาดให้ถูกวิธีด้วย try/except/else/finally — ไม่ใช่แค่ครอบ try กว้าง ๆ แล้วเงียบ
โปรแกรมจริงต้องเจอสถานการณ์ที่ผิดพลาด: ไฟล์ไม่มี, ผู้ใช้กรอกผิด, เน็ตหลุด การจัดการ exception ที่ดีทำให้โค้ด "พังอย่างสง่างาม" บอกได้ว่าพังเพราะอะไร แทนที่จะ crash เงียบ ๆ หรือกลืน error จนหา bug ไม่เจอ
คุณเคยเห็น try/except มาแล้ว หัวข้อนี้ลงลึกกว่า: จับเฉพาะชนิด, ลำดับ else/finally, exception hierarchy และวิธีที่มืออาชีพใช้จริง
โครงสร้างเต็ม: try / except / else / finally
ทั้ง 4 ส่วนมีหน้าที่ต่างกันชัดเจน — try คือโค้ดเสี่ยง, except จับเมื่อพัง, else รันเมื่อไม่พัง, finally รันเสมอ
try:
x = int(input("กรอกเลข: ")) # โค้ดที่อาจพัง
except ValueError:
print("นั่นไม่ใช่เลข") # ทำเมื่อเกิด ValueError
else:
print(f"ดีมาก ได้ {x}") # ทำเมื่อ try ไม่พัง
finally:
print("จบการทำงาน") # ทำเสมอ ไม่ว่าพังหรือไม่| ส่วน | ทำงานเมื่อ | ใช้ทำ |
|---|---|---|
| try | เริ่มเสมอ | โค้ดที่อาจเกิด error |
| except | เกิด error ตรงชนิด | จัดการ error |
| else | try สำเร็จ ไม่พัง | โค้ดต่อที่ขึ้นกับ try สำเร็จ |
| finally | เสมอ (พังหรือไม่ก็ตาม) | คืน resource / ปิดของ |
จับเฉพาะชนิด — อย่าจับกว้างเกินไป
จับ exception ให้ตรงชนิดที่คาดไว้ และเก็บตัว exception ด้วย as เพื่อดูรายละเอียด จับหลายชนิดได้ทั้งแบบแยกและรวม
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
print("หารด้วยศูนย์ไม่ได้")
except TypeError as e:
print(f"ชนิดข้อมูลผิด: {e}")
# จับหลายชนิดพร้อมกันด้วย tuple
try:
risky()
except (ValueError, KeyError) as e:
print(f"เกิดข้อผิดพลาด: {e}")Exception Hierarchy
exception ทุกตัวสืบทอดจาก Exception การจับ exception แม่จะจับลูกทั้งหมดด้วย — มีประโยชน์แต่ต้องระวังไม่ให้กว้างเกิน
# ValueError และ KeyError เป็นลูกของ Exception
# จับ Exception = จับเกือบทุกอย่าง (กว้างมาก ใช้เท่าที่จำเป็น)
try:
risky()
except Exception as e:
print(f"ผิดพลาด: {type(e).__name__}: {e}")
# log ไว้แล้วอาจ re-raise ต่อ
raise # โยน exception เดิมต่อ (re-raise)อย่าเขียน except: เปล่า ๆ หรือ except Exception แล้ว pass เฉย ๆ เพราะมันกลืน error ทุกอย่างรวมถึง bug ของเราเอง ทำให้โปรแกรมผิดแบบเงียบ ๆ หา bug ไม่เจอ — จับให้ตรงชนิด และอย่างน้อยต้อง log
# ❌ ห้ามทำ: กลืนทุก error เงียบ ๆ
try:
do_something()
except:
pass # bug ซ่อนอยู่ตรงนี้ไม่มีวันเจอ
# ✅ จับตรงชนิด + จัดการชัดเจน
try:
do_something()
except FileNotFoundError as e:
print(f"ไม่พบไฟล์: {e}")สรุปหัวข้อนี้
- try (เสี่ยง) / except (จับ) / else (สำเร็จ) / finally (เสมอ)
- จับเฉพาะชนิดที่คาดไว้ เก็บด้วย as e เพื่อดูรายละเอียด
- exception สืบทอดกัน — จับแม่ = จับลูกทั้งหมด
- ห้าม except เปล่าหรือ pass เงียบ ๆ — มันซ่อน bug
1) เขียนฟังก์ชันหารที่จัดการทั้ง ZeroDivisionError และ TypeError 2) เขียนโค้ดอ่านเลขจาก input ที่ใช้ครบ try/except/else/finally 3) ลองจับ (ValueError, KeyError) ด้วย tuple เดียว 4) อธิบายว่าทำไม except: pass เป็นอันตราย