On this page
ข้อผิดพลาด & การแก้บั๊ก (Errors & Debugging)
ขอบเขตตัวแปร (scope), อ่าน error ให้เป็น, จัดการด้วย try/except และเทคนิคแก้บั๊กที่ใช้จริง
การเจอ error เป็นเรื่องปกติของทุกคนที่เขียนโปรแกรม ไม่ใช่ความล้มเหลว มืออาชีพต่างจากมือใหม่ตรงที่ "อ่าน error เป็น" และหาสาเหตุได้เร็ว หัวข้อนี้ปิดท้ายบทที่ 2 ด้วยทักษะเอาตัวรอดที่สำคัญที่สุด
ขอบเขตของตัวแปร (Scope)
ตัวแปรที่สร้างในฟังก์ชัน (local) ใช้ได้แค่ในฟังก์ชันนั้น ส่วนตัวแปรนอกฟังก์ชัน (global) เข้าถึงได้ทั่วไป เข้าใจเรื่องนี้ช่วยแก้บั๊ก "ทำไมตัวแปรหาไม่เจอ" ได้มาก
total = 100 # global — ใช้ได้ทั้งไฟล์
def calc():
x = 10 # local — มีอยู่แค่ในฟังก์ชันนี้
return total + x # อ่าน global ได้
print(calc()) # 110
# print(x) # NameError! x ไม่มีอยู่นอกฟังก์ชันเพื่อไม่ให้ตัวแปรชื่อซ้ำกันในคนละฟังก์ชันชนกัน แต่ละฟังก์ชันมี "พื้นที่ส่วนตัว" ของมัน ทำให้เขียนฟังก์ชันได้อย่างมั่นใจว่าไม่ไปแก้ค่าของที่อื่นโดยบังเอิญ
ประเภทของ error ที่เจอบ่อย
| Error | สาเหตุ | ตัวอย่าง |
|---|---|---|
| SyntaxError | พิมพ์ผิดไวยากรณ์ | ลืม : ท้าย if, วงเล็บไม่ครบ |
| NameError | ใช้ชื่อที่ยังไม่มี/พิมพ์ผิด | เรียก prnt() แทน print() |
| TypeError | ใช้ชนิดข้อมูลผิด | "3" + 5 (str บวก int) |
| ValueError | ชนิดถูกแต่ค่าผิด | int("abc") |
| IndexError | เข้าถึง index เกินขนาด | list[10] บนลิสต์ 3 ตัว |
| KeyError | เข้าถึง key ที่ไม่มีใน dict | d["missing"] |
| ZeroDivisionError | หารด้วยศูนย์ | 10 / 0 |
| IndentationError | ย่อหน้าผิด | ย่อหน้าไม่ตรงกัน |
อ่าน error message ให้เป็น
เมื่อโปรแกรม error Python จะพิมพ์ข้อความบอกว่าเกิดที่บรรทัดไหนและเพราะอะไร อ่านจาก "ล่างขึ้นบน" บรรทัดสุดท้ายคือชนิดและคำอธิบาย ส่วนเหนือขึ้นไปบอกตำแหน่ง
Traceback (most recent call last):
File "main.py", line 3, in <module>
print(10 / 0)
ZeroDivisionError: division by zero
# อ่านว่า: บรรทัด 3 ในไฟล์ main.py หารด้วยศูนย์จัดการ error ด้วย try / except
บาง error เราคาดเดาได้ว่าจะเกิด (เช่นผู้ใช้พิมพ์ตัวอักษรแทนตัวเลข) ใช้ try/except ดักไว้ ไม่ให้โปรแกรมพังกลางคัน
try:
num = int(input("ใส่ตัวเลข: "))
print(f"100 หาร {num} = {100 / num}")
except ValueError:
print("กรุณาใส่ตัวเลขเท่านั้น")
except ZeroDivisionError:
print("หารด้วยศูนย์ไม่ได้")
else:
print("คำนวณสำเร็จ") # ทำเมื่อไม่มี error
finally:
print("จบการทำงาน") # ทำเสมอ ไม่ว่าจะ error หรือไม่การเขียน except: เฉย ๆ (จับทุก error) ทำให้บั๊กถูกซ่อนและหายาก ควรระบุชนิด error ที่คาดไว้ เช่น except ValueError: เพื่อให้รู้ว่ากำลังจัดการอะไรอยู่
โยน error เองด้วย raise
นอกจากดัก error เรายัง "โยน" error เองได้เมื่อเจอสถานการณ์ที่ไม่ควรดำเนินต่อ — ดีกว่าคืนค่าแปลก ๆ (เช่น -1) ที่ผู้เรียกอาจลืมเช็ค ทำให้ bug ปรากฏทันทีตรงจุดเกิดเหตุ
def set_age(age):
if age < 0:
raise ValueError(f"อายุติดลบไม่ได้: {age}") # หยุดและบอกสาเหตุชัด
return age
# set_age(-5) -> ValueError: อายุติดลบไม่ได้: -5
# ใช้คู่กับ try/except ของผู้เรียกได้
try:
set_age(-5)
except ValueError as e:
print(f"ผิดพลาด: {e}")การ raise ตั้งแต่เจอข้อมูลผิดที่ "ขอบ" ของระบบ ดีกว่าปล่อยให้ค่าผิดไหลไปพังที่อื่นซึ่งหาสาเหตุยากกว่ามาก นี่คือหลักการ defensive programming ที่จะลงลึกในคอร์สเชิงลึก
เทคนิคแก้บั๊กที่ใช้จริงทุกวัน
- อ่าน error message ให้จบ — มันบอกบรรทัดและสาเหตุตรง ๆ
- แทรก print() ดูค่าตัวแปรในจุดต่าง ๆ ว่าตรงกับที่คิดไหม (วิธีที่ง่ายและทรงพลังที่สุด)
- ตัดโค้ดให้เหลือน้อยที่สุดที่ยังเกิดบั๊ก เพื่อจำกัดวงหาสาเหตุ
- ตรวจ assumption: ชนิดข้อมูลถูกไหม (ใช้ type()), ค่าว่างไหม, index เกินไหม
- อธิบายโค้ดให้คนอื่น (หรือเป็ดยาง) ฟังทีละบรรทัด มักเจอจุดผิดเอง
# ตัวอย่างใช้ print debug: ทำไมผลรวมผิด?
def sum_list(nums):
total = 0
for n in nums:
print(f"กำลังบวก {n}, total ตอนนี้ = {total}") # debug
total += n
return total
sum_list([1, 2, 3])
# เห็นค่าทีละขั้น ช่วยจับจุดที่ผิดได้สรุปหัวข้อนี้ & จบบทที่ 2
- ตัวแปร local อยู่แค่ในฟังก์ชัน, global เข้าถึงได้ทั่วไป
- รู้จัก error ยอดฮิต: NameError, TypeError, ValueError, IndexError, KeyError
- อ่าน error จากล่างขึ้นบน บรรทัดสุดท้ายบอกชนิดและสาเหตุ
- try/except ดัก error ที่คาดได้ ไม่ให้โปรแกรมพัง — ระบุชนิดให้ชัด
- เทคนิคแก้บั๊ก: อ่าน error, print ดูค่า, ตัดโค้ด, ตรวจ assumption
1) เขียนโปรแกรมหารเลข 2 ตัวที่ดักทั้ง ValueError และ ZeroDivisionError 2) ลองสร้าง error แต่ละชนิดในตารางดูว่าหน้าตา message เป็นอย่างไร 3) แก้บั๊กโปรแกรมที่ให้มาด้วยการแทรก print ดูค่า 4) เขียนฟังก์ชันรับ input จนกว่าผู้ใช้จะใส่ตัวเลขถูกต้อง (ใช้ while + try/except)
ตอนนี้คุณมีเครื่องมือครบสำหรับเขียนโปรแกรมเชิงตรรกะ: ตัวแปร, operator, เงื่อนไข, loop, ฟังก์ชัน และจัดการ error ได้ บทถัดไปจะเรียนวิธีเก็บข้อมูลหลายค่าอย่างเป็นระบบ