ในเนื้อหา
ไพธอนเบื้องต้นบทที่ ๒๕ เรื่องของการจัดการกับข้อยกเว้นได้พูดถึงการใช้
try
,
except
และ
else
ไป แต่จริงๆแล้วโครงสร้างการจัดการกับข้อยกเว้นนี้ยังประกอบไปด้วยอีกตัวคือ
finally
ซึ่งอาจใช้ไม่บ่อยและไม่มีความสำคัญมาก จึงไม่ได้เขียนในเนื้อหาหลักแต่แยกมาเขียนเป็นเนื้อหาเสริม อาจไม่ได้จำเป็นต้องใช้งานจริง แต่ถ้ารู้ไว้เวลามีคนใช้ก็จะเข้าใจถึงความหมาย
ความเข้าใจเบื้องต้น โดยทั่วไปแล้วโครงสร้างการจัดการกับข้อยกเว้นจะประกอบขึ้นจาก
try
และ
except
เป็นหลัก โดยใส่คำสั่งที่ระวังว่าอาจจะเกิดข้อยกเว้นไว้ใน
try
และใส่คำสั่งที่จะให้ทำเมื่อมีข้อยกเว้นลงใน
except
แต่นอกจากนั้นแล้วยังสามารถใส่
finally
ต่อลงไปได้อีก โดยส่วนที่อยู่ใน
finally
นั้นจะทำตอนท้ายสุดอย่างแน่นอนไม่ว่าจะเกิดอะไรขึ้น
โครงสร้าง
try
,
except
พร้อม
finally
อาจเขียนได้ดังนี้
try:
คำสั่งที่กังวลว่าจะเกิดข้อยกเว้นขึ้นได้
except:
คำสั่งสำหรับจัดการกับข้อยกเว้น
finally:
คำสั่งที่จะให้ทำงานเสมอตอนท้ายที่สุด
เพียงแต่ว่าถ้ามาลองคิดดูดีๆแล้ว ถ้าเราแค่มีคำสั่งที่ต้องการจะให้ทำตอนท้ายสุดหลังจากเนื้อหาใน
try
และ
except
แล้วละก็ มีความจำเป็นอะไรต้องเขียนใน
finally
ด้วยหรือ? เขียนข้างนอกมันก็ทำงานเหมือนกันไม่ใช่หรือ?
ตัวอย่างให้พอเห็นภาพ
try:
print('เดินไปโรงเรียน')
raise Exception('หกล้มหัวคะมำ')
except Exception as e:
print(e)
print('ลุกขึ้นมาแล้วเดินต่อ')
finally:
print('ในที่สุดก็ถึงโรงเรียน')
print('ตั้งใจเรียน')
ผลที่ได้
เดินไปโรงเรียน
หกล้มหัวคะมำ
ลุกขึ้นมาแล้วเดินต่อ
ในที่สุดก็ถึงโรงเรียน
ตั้งใจเรียน
ในที่นี้ทั้งส่วนของ
print('ในที่สุดก็ถึงโรงเรียน')
และ
print('ตั้งใจเรียน')
ก็ทำตอนท้ายไม่ว่าจะเกิดข้อยกเว้นหรือไม่ก็ตาม
ดังนั้นจะเห็นได้ว่าการใช้
finally
ในกรณีแบบนี้ไม่ได้มีความจำเป็น เพราะสามารถเขียนข้างนอกได้
อย่างไรก็ตาม ความแตกต่างระหว่างการเขียนภายใน
finally
กับการเขียนข้างนอกจะเกิดขึ้นได้ในหลายกรณีด้วยกัน ในกรณีแบบนั้นจึงจะมีความหมายที่จะใช้
finally
ซึ่งจะยกตัวอย่างต่อไปนี้
finally จะทำงานแม้เกิดข้อยกเว้นขึ้นอีกใน except ดังที่ได้กล่าวไปว่าบทบาทของ
finally
ก็คือใช้ใส่คำสั่งที่ยังไงก็ต้องการให้ทำตอนท้ายสุดอย่างแน่นอนไม่ว่าจะเกิดอะไรขึ้นก็ตาม
ซึ่งยังรวมถึงกรณีที่เกิดข้อยกเว้นขึ้นซ้ำซ้อนภายในโครงสร้าง
except
ด้วย
ปกติแล้วถ้าเกิดข้อยกเว้นในโครงสร้าง
try
ข้อยกเว้นนั้นก็จะถูกยกเลิกแล้วโปรแกรมก็จะทำงานต่อไปได้โดยเข้าสู่โครงสร้าง
except
ไปทำคำสั่งในนั้น แต่ว่าถ้าหากภายในส่วนของ
except
นั้นยังเกิดข้อยกเว้นขึ้นมาอีก คราวนี้โปรแกรมจะหยุดทำงานไปจริงๆ และแน่นอนว่าพอเป็นแบบนี้แล้วคำสั่งที่ถูกเขียนอยู่ต่อจากนั้นก็จะไม่ทำงาน
แต่ว่าถ้าหากเขียนเอาไว้ใน
finally
แล้วละก็ คำสั่งนั้นจะทำงานขึ้นมาก่อนที่โปรแกรมจะหยุดทำงานไปจริงๆ
ตัวอย่าง
try:
raise Exception
except:
raise Exception
finally:
print('ส่วนนี้จะทำงาน')
print('ส่วนนี้ไม่ทำงาน')
อย่างไรก็ตาม
finally
ไม่ได้ช่วยให้ข้อยกเว้นที่เกิดขึ้นในโครงสร้าง
except
ถูกยกเลิกไป เพียงแต่อย่างน้อยก็ช่วยให้ทำคำสั่งบางอย่างที่ต้องการทำให้ได้ก่อนแม้ว่าโปรแกรมจะมีอันต้องหยุดลง
ดังนั้นแล้วถ้ามีคำสั่งสำคัญที่ต้องการทำตอนท้ายเสมอละก็อาจใส่ใน
finally
แบบนี้ก็จะมั่นใจได้ว่าจะทำงานจริงๆเสมอ
การใช้ try finally โดยละ except อีกกรณีที่อาจใช้
finally
ก็คือเมื่อจะใช้
try
โดยไม่ใช่
except
ปกติแล้ว
try
จะใช้กับ
except
เสมอ ไม่สามารถเขียนเดี่ยวๆได้ แม้ว่าจะไม่ได้มีเนื้อหาอะไรที่ต้องการให้จัดการเมื่อเจอข้อยกเว้นก็ตาม
แต่ว่านอกจากนี้แล้ว
try
ยังใช้คู่กับ
finally
เฉยๆได้ด้วย โดยที่ไม่มี
except
แบบนี้ก็จะกลายเป็นโครงสร้าง
try
finally
เพียงแต่ว่าถ้าไม่มี
except
ละก็ ข้อยกเว้นที่เกิดขึ้นใน
try
ก็จะไม่ถูกยกเลิกและโปรแกรมก็จะหยุดการทำงาน
ดังนั้นความหมายของการใช้โครงสร้าง
try
finally
ก็คือเมื่อมีคำสั่งบางอย่างที่ต้องการให้ทำแม้ว่าจะมีข้อยกเว้นใน
try
แต่ว่าไม่จำเป็นต้องหักล้างข้อยกเว้นนั้น แต่ให้โปรแกรมหยุดทำงานไป
ซึ่งถ้าเขียนด้วยโครงสร้าง
try
except
ละก็อาจเขียนในลักษณะนี้
try:
# คำสั่งบางอย่าง
except Exception as e:
# คำสั่งที่ต้องการให้ทำเมื่อเกิดข้อยกเว้นใน try ก่อนจะหยุดการทำงาน
raise e # ปล่อยให้เกิดข้อยกเว้นแล้วหยุดการทำงานไป
แต่ถ้าเชียนใหม่โดยใช้
try
finally
ก็จะเขียนได้แบบนี้ ซึ่งสั้นลง
try:
# คำสั่งบางอย่าง
finally:
# คำสั่งที่ต้องการให้ทำเมื่อเกิดข้อยกเว้นใน try ก่อนจะหยุดการทำงาน
ตัวอย่างเช่น
def a_han_b(a,b):
try:
print(a/b)
finally:
print('หารเสร็จแล้ว')
a_han_b(3,0)
แบบนี้จะมีข้อความ
หารเสร็จแล้ว
ปรากฎขึ้นมาก่อนที่จะตามมาด้วยข้อความประกาศว่ามีข้อยกเว้นเกิดขึ้น
กรณีที่มีการ return ใน finally ภายในฟังก์ชัน ถึงจะเพิ่งบอกไปว่า
finally
นั้นไม่ได้มีส่วนช่วยในการลบล้างข้อยกเว้นที่เกิดขึ้น ถึงคำสั่งในโครงสร้าง
finally
จะทำงานก่อนเกิดข้อยกเว้น แต่ยังไงหลังจากนั้นโปรแกรมก็จะหยุดลง แต่อย่างไรก็ตาม ถ้าหาก
finally
ถูกใช้ภายในโครงสร้างฟังก์ชัน มันสามารถทำให้ข้อยกเว้นที่เกิดขึ้นถูกลบล้างได้จริงๆ โดยการใส่
return
ในนั้น เช่น
def f():
try:
raise Exception
finally:
return
f()
หรือกรณีที่มีข้อยกเว้นขึ้นใน
except
อีกก็ด้วย
def f():
try:
raise Exception
except:
raise Exception
finally:
return
f()
กรณีแบบนี้แมว่าจะมีการโยนข้อยกเว้นลงใน
try
แต่เพราะมี
return
อยู่ใน
finally
ทำให้ข้อยกเว้นถูกลบล้างหายไปและโปรแกรมก็ทำงานไปได้ตามปกติ
หมายความว่าถ้าภายในฟังก์ชันมีการใส่
return
ลงไปใน
finally
ก็จะไม่หยุดการทำงานกลางคันไม่ว่าจะเกิดข้อยกเว้นในโครงสร้าง
try
หรือ
except
ก็ตาม
แม้มีการ return ใน try หรือ except ปกติแล้วเมื่อมีการใช้คำสั่ง
return
ภายในฟังก์ชันขึ้นมา การทำงานในฟังก์ชั้นนั้นจะสิ้นสุดลงทันที
แต่กรณีที่ใช้โครงสร้าง
try
finally
ในฟังก์ชัน แล้วเกิดการ
return
ภายในโครงสร้าง
try
สิ่งที่เขียนไว้ใน
finally
ก็ยังจะทำงานด้วยตอนจบฟังก์ชัน
def f():
try:
return
finally:
print('คำสั่งในส่วนนี้ทำงานก่อนจบฟังก์ชัน')
f()
ดังนั้นความหมายของ
finally
ที่ว่ามีใช้เพื่อยืนยันว่าจะทำงานอย่างแน่นอนตอนท้ายสุดนั้น ไม่ใช่แค่ใช้ในการจัดการข้อยกเว้นเท่านั้น แต่ยังใช้ในกรณีที่ไม่เกี่ยวกับข้อยกเว้นได้ด้วย คือใช้กับฟังก์ชันเมื่อมีคำสั่งที่ต้องการให้ทำงานเสมอตอนจบฟังก์ชัน
นอกจากนี้แล้ว ในกรณีที่มีการ
return
ภายใน
except
ก็เช่นกัน ยังไงคำสั่งในโครงสร้าง
finally
ก็ทำงาน
def f():
try:
raise Exception
except:
return
finally:
print('คำสั่งในส่วนนี้ทำงานก่อนจบฟังก์ชัน')
f()
ค่าที่ return ใน finally จึงถูกนำไปใช้ สุดท้ายนี้ น่าคิดดูว่าถ้าหากมีการ
return
เกิดขึ้นทั้งในโครงสร้าง
try
,
except
และ
finally
แล้วจะเป็นอย่างไร
ลองดูตัวอย่างนี้
def f(x):
try:
if(x == 0):
return 'พยายาม'
else:
raise Exception
except:
return 'ยกเว้น'
finally:
return 'จบแล้ว'
print(f(0)) # จบแล้ว
print(f(1)) # จบแล้ว
จะเห็นได้ว่าไม่ว่ายังไงค่าที่
return
ใน
finally
ก็จะถูกนำไปใช้เสมอ แม้ว่าก่อนหน้านั้นจะเกิดการ
return
ขึ้นก่อนภายใน
try
หรือ
except
ก็ตาม แต่หลังจากนั้น
finally
ก็ทำงานขึ้นมาแล้วก็เจอ
return
ในนั้นแล้วค่าเดิมที่
return
ไปก็จะถูกเขียนทับไป
เห็นแบบนี้แล้วรู้เลยว่า
finally
นั้นช่างแข็งแกร่ง
อย่างไรก็ตาม ผู้เชี่ยวชาญแนะนำว่าไม่ควรใส่
return
ภายใน
finally
เพราะแม้ว่าจะไม่ผิดไวยากรณ์ แต่มักจะนำไปสู่ผลลัพธ์ที่ไม่ได้คาดหวัง ดังนั้นจึงแค่รู้ไว้เฉยๆก็พอว่าถ้าทำแบบนี้แล้วจะเกิดผลแบบนี้ขึ้นได้ แต่ว่าถ้าไม่จำเป็นก็ไม่น่าจะนำไปใช้จริงๆ
สรุปทิ้งท้าย จากที่เขียนมานั้นคงจะมองเห็นภาพกันแล้วว่า
finally
นั้นมีความหมายยังไง ใช้งานยังไงบ้าง
เห็นบางตำราหรือบทความเขียนอธิบายความหมายของ
finally
ว่าเป็น "ส่วนที่จะทำงานไม่ว่าจะเกิดข้อยกเว้นหรือไม่" ซึ่งการอธิบายแบบนี้แม้ว่าจะไม่ผิด แต่ก็ไม่สมบูรณ์ เพราะถ้าแค่นั้นจริงๆก็ไม่ได้มีความจำเป็นอะไรที่จะต้องใช้
finally
แค่เขียนไว้ด้านนอกก็ได้เหมือนกัน
แต่ว่าดังที่ได้อธิบายมาจะเห็นว่า
finally
เป็นอะไรที่มากกว่านั้น ดังนั้นถ้าจะให้ดีจริงๆควรรอธิบายเสริมในส่วนนี้ไปด้วย เช่นอาจอธิบายว่าเป็น
"ส่วนที่จะทำงานเสมอในท้ายที่สุดแม้ว่าจะเกิดข้อยกเว้นหรือการ return ขึ้นใน try หรือ except" อ้างอิง