On this page
Defensive Programming
ตรวจสอบ input ที่ขอบระบบและเขียนโค้ดที่ "พังเร็ว พังชัด" แทนที่จะคำนวณผิดเงียบ ๆ
defensive programming คือการคิดล่วงหน้าว่า "อะไรพังได้บ้าง" แล้วป้องกันไว้ที่จุดที่ข้อมูลเข้าระบบ หลักการคือ fail fast — ถ้าข้อมูลผิดให้หยุดทันทีที่จุดเกิดเหตุ ดีกว่าปล่อยให้ค่าผิดไหลไปพังที่อื่นซึ่งหายากกว่า
ตรวจ input ที่ขอบระบบ (validate)
ข้อมูลจากภายนอก (ผู้ใช้, ไฟล์, API) เชื่อถือไม่ได้ ให้ตรวจที่จุดรับเข้าก่อนนำไปใช้ — ภายในระบบที่ตรวจแล้วค่อยไว้ใจได้
def create_account(name: str, age: int):
if not name or not name.strip():
raise ValueError("ชื่อต้องไม่ว่าง")
if not isinstance(age, int):
raise TypeError("อายุต้องเป็นจำนวนเต็ม")
if not 0 < age < 150:
raise ValueError(f"อายุไม่สมเหตุสมผล: {age}")
return {"name": name.strip(), "age": age}
create_account("", 25) # ValueError: ชื่อต้องไม่ว่าง (พังทันทีตรงจุด)EAFP vs LBYL — สองสไตล์การตรวจ
มี 2 แนวคิด: LBYL (เช็คก่อนทำ) และ EAFP (ลองทำเลย แล้วจับ error) — Python นิยม EAFP เพราะอ่านง่ายกว่าและเลี่ยง race condition
data = {"name": "Aph"}
# LBYL — Look Before You Leap (เช็คก่อน)
if "age" in data:
age = data["age"]
else:
age = 0
# EAFP — Easier to Ask Forgiveness than Permission (ลองเลย จับทีหลัง)
try:
age = data["age"]
except KeyError:
age = 0
# จริง ๆ เคสนี้ Pythonic สุดคือ:
age = data.get("age", 0)| สไตล์ | แนวคิด | เหมาะกับ |
|---|---|---|
| LBYL | เช็คเงื่อนไขก่อนลงมือ | เงื่อนไขชัดเจน ไม่ซับซ้อน |
| EAFP | ลองทำแล้วจับ error | งาน I/O, dict/attribute access (Pythonic) |
assert — ตรวจสมมติฐานระหว่างพัฒนา
assert ใช้ตรวจสิ่งที่ "ควรจะเป็นจริงเสมอ" ในโค้ด ถ้าไม่จริงจะ raise AssertionError ทันที เหมาะตรวจ logic ภายในระหว่างพัฒนา
def apply_discount(price, percent):
assert 0 <= percent <= 100, f"เปอร์เซ็นต์ผิด: {percent}"
return price * (1 - percent / 100)
apply_discount(100, 150) # AssertionError: เปอร์เซ็นต์ผิด: 150assert ถูก "ปิด" ทั้งหมดเมื่อรันด้วย python -O (optimized mode) ดังนั้นถ้าใช้ assert ตรวจ input จากผู้ใช้ พอรันโหมด -O การตรวจจะหายไปและข้อมูลผิดจะหลุดเข้าระบบ — ใช้ assert กับ "สมมติฐานภายในของโปรแกรมเมอร์" เท่านั้น ส่วน input ผู้ใช้ให้ใช้ if + raise
สรุปหัวข้อนี้
- validate ข้อมูลจากภายนอกที่ขอบระบบ — fail fast พังตรงจุดเกิดเหตุ
- LBYL เช็คก่อนทำ, EAFP ลองแล้วจับ — Python นิยม EAFP
- assert ตรวจสมมติฐานภายในระหว่างพัฒนา
- ห้ามใช้ assert ตรวจ input ผู้ใช้ (ถูกปิดด้วย python -O) — ใช้ if + raise
1) เขียนฟังก์ชัน register(email, password) ที่ validate: email มี @, password ยาว >= 8 2) เขียนการเข้าถึง dict ทั้งแบบ LBYL และ EAFP แล้วเทียบ 3) ใช้ assert ตรวจว่าผลลัพธ์ภายในฟังก์ชันไม่ติดลบ 4) อธิบายว่าทำไมไม่ควรใช้ assert ตรวจ input ผู้ใช้