ฉันควรระบุประเภทข้อยกเว้นในคำสั่ง "except" เสมอหรือไม่


96

เมื่อใช้ PyCharm IDE ใช้except:โดยไม่มีการยกเว้นประเภททริกเกอร์การแจ้งเตือนจาก IDE Too broadที่ข้อยกเว้นนี้เป็น

ฉันควรเพิกเฉยต่อคำแนะนำนี้หรือไม่? หรือว่า Pythonic ระบุประเภทข้อยกเว้นเสมอ?


หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้มากกว่าคำตอบ Google กลืนข้อยกเว้น คุณสามารถทำสิ่งที่น่าสนใจและไม่ควรทำควบคู่ไปกับพวกเขาได้ทุกประเภท รหัสกลิ่นเป็นอีกหนึ่ง
Tony Hopkinson

คำตอบ:


93

การระบุประเภทข้อยกเว้นอย่างชัดเจนมักจะดีกว่าเสมอ หากคุณใช้except:ประโยคเปล่าคุณอาจพบข้อยกเว้นอื่นนอกเหนือจากที่คุณคาดว่าจะจับได้ซึ่งสามารถซ่อนข้อบกพร่องหรือทำให้ยากต่อการดีบักโปรแกรมเมื่อพวกเขาไม่ได้ทำตามที่คุณคาดหวัง

ตัวอย่างเช่นหากคุณกำลังแทรกแถวลงในฐานข้อมูลคุณอาจต้องการตรวจจับข้อยกเว้นที่ระบุว่ามีแถวนั้นอยู่แล้วดังนั้นคุณจึงสามารถทำการอัปเดตได้

try:
    insert(connection, data)
except:
    update(connection, data)

หากคุณระบุ bare except:คุณจะตรวจพบข้อผิดพลาดของซ็อกเก็ตที่ระบุว่าเซิร์ฟเวอร์ฐานข้อมูลล้มลง ทางที่ดีควรจับเฉพาะข้อยกเว้นที่คุณรู้วิธีจัดการ - มักจะดีกว่าที่โปรแกรมจะล้มเหลว ณ จุดที่เป็นข้อยกเว้นแทนที่จะดำเนินการต่อ แต่ทำงานในรูปแบบที่ไม่คาดคิดแปลก ๆ

กรณีหนึ่งที่คุณอาจต้องการใช้ bare except:คือที่ระดับบนสุดของโปรแกรมที่คุณต้องใช้งานอยู่เสมอเช่นเซิร์ฟเวอร์เครือข่าย แต่คุณต้องระมัดระวังในการบันทึกข้อยกเว้นมิฉะนั้นจะไม่สามารถระบุได้ว่าเกิดอะไรขึ้น โดยทั่วไปควรมีเพียงที่เดียวในโปรแกรมที่ทำเช่นนี้

ควันหลงทั้งหมดนี้คือรหัสของคุณไม่ควรทำraise Exception('some message')เพราะมันบังคับให้รหัสลูกค้ากับการใช้งานexcept:(หรือexcept Exception:ซึ่งเป็นเกือบเป็นเลว) คุณควรกำหนดข้อยกเว้นเฉพาะสำหรับปัญหาที่คุณต้องการส่งสัญญาณ (อาจสืบทอดมาจากคลาสย่อยข้อยกเว้นในตัวเช่นValueErrorหรือTypeError) หรือคุณควรเพิ่มข้อยกเว้นในตัวที่เฉพาะเจาะจง สิ่งนี้ช่วยให้ผู้ใช้รหัสของคุณระมัดระวังในการจับเฉพาะข้อยกเว้นที่ต้องการจัดการ


7
+1 จริงมาก สนุกยิ่งขึ้นกับตัวอย่าง: except:จับได้ (เหนือสิ่งอื่นใด) NameErrorและAttributeErrorดังนั้นหากคุณสะกดคำผิดในtryบล็อก (เช่นฟังก์ชัน "แทรก" ของคุณถูกเรียกจริงinsert_oneเพราะมีคนไม่ให้ความสำคัญกับความสม่ำเสมอเท่าที่ควร) update()มักจะพยายามที่จะเงียบ

1
แล้วเมื่อไหร่ที่คุณต้องแน่ใจว่าจะไม่มีการโยนข้อยกเว้นเหนือไซต์การโทรปัจจุบัน? เช่น - ฉันตรวจพบข้อยกเว้นเฉพาะทั้งหมดที่ฉันคาดการณ์ได้ว่าถูกโยนตอนนี้ฉันต้องเพิ่มว่า "ถ้าสิ่งนี้ฉันไม่คิดว่าจะถูกโยนฉันต้องบันทึกก่อนที่มันจะฆ่าบริบทการดำเนินการที่กำลังทำงานอยู่" (เช่นmain())?
Adam Parkin

นั่นคือสถานการณ์ที่ฉันกำลังพูดถึงในย่อหน้าที่ขึ้นต้น 'One case ... ' ในบางครั้งจำเป็นอย่างยิ่ง แต่โปรแกรมใด ๆ ที่กำหนดควรมีเพียงที่เดียวที่ทำเช่นนั้น และคุณต้องระวังให้ชัดเจนในบันทึกว่ากำลังเกิดอะไรขึ้นมิฉะนั้นคุณจะมีช่วงเวลาที่น่าหงุดหงิดในการพยายามหาสาเหตุที่โปรแกรมของคุณไม่จัดการเหตุการณ์ / คำขอ / อะไรก็ตามอย่างถูกต้อง
babbageclunk

3
@ เดลแนน: แย่กว่านั้น except Exception:จะจับNameErrorและAttributeErrorเกินไป สิ่งที่ทำให้การเปลือยexcept:ไม่ดีคือการจับสิ่งของที่ไม่มีธุรกิจถูกจับได้เช่นSystemExit(ยกขึ้นเมื่อคุณโทรexitหรือsys.exitและตอนนี้คุณได้ป้องกันการออกจากจุดประสงค์แล้ว) และKeyboardInterrupt(อีกครั้งหากผู้ใช้โดนCtrl-Cคุณอาจไม่ต้องการ เพื่อให้ทำงานต่อไปเพื่อทำลายพวกเขา) เฉพาะอย่างหลังเท่านั้นที่สมเหตุสมผลที่จะจับและควรจับอย่างชัดเจน อย่างน้อยก็except Exception:ให้ทั้งสองเผยแพร่ตามปกติ
ShadowRanger

นี่เป็นคำแนะนำผ้าสำลีที่ฉันไม่เห็นด้วยอย่างสม่ำเสมอ บ่อยครั้งที่คุณต้องการตอบสนองในลักษณะเดียวกันไม่ว่าจะมีข้อยกเว้นแบบใดก็ตาม ฉันรู้สึกว่านี่เป็นการระงับคำแนะนำเฉพาะบางอย่างจาก Java ในยุคแรก ๆ ที่ไม่เคยปล่อยไป
enl8enmentnow

39

คุณไม่ควรเพิกเฉยต่อคำแนะนำที่ล่ามให้กับคุณ

จากPEP-8 Style Guide สำหรับ Python:

เมื่อจับข้อยกเว้นให้พูดถึงข้อยกเว้นเฉพาะเมื่อทำได้แทนที่จะใช้ bare except: clause

ตัวอย่างเช่นใช้:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

ข้อยกเว้นเปล่า: อนุประโยคจะตรวจจับข้อยกเว้น SystemExit และ KeyboardInterrupt ทำให้ขัดขวางโปรแกรมด้วย Control-C ได้ยากขึ้นและสามารถปกปิดปัญหาอื่น ๆ หากคุณต้องการตรวจจับข้อยกเว้นทั้งหมดที่แสดงข้อผิดพลาดของโปรแกรมให้ใช้ข้อยกเว้น Exception: (bare except เทียบเท่ากับยกเว้น BaseException :)

หลักการง่ายๆคือ จำกัด การใช้ประโยค 'ยกเว้น' เปล่าไว้สองกรณี:

หากตัวจัดการข้อยกเว้นจะพิมพ์ออกมาหรือบันทึกการย้อนกลับ อย่างน้อยผู้ใช้จะทราบว่ามีข้อผิดพลาดเกิดขึ้น หากรหัสจำเป็นต้องทำการล้างข้อมูลบางอย่าง แต่ปล่อยให้ข้อยกเว้นแพร่กระจายขึ้นไปด้วยการเพิ่ม ลอง ... ในที่สุดก็เป็นวิธีที่ดีกว่าในการจัดการกับคดีนี้



9

ไม่ใช่เฉพาะสำหรับ Python นี้

จุดรวมของข้อยกเว้นคือการจัดการกับปัญหาให้ใกล้เคียงกับที่เกิดมากที่สุด

ดังนั้นคุณจึงเก็บรหัสที่อาจเกิดขึ้นในสถานการณ์พิเศษอาจทำให้เกิดปัญหาและความละเอียด "ถัดไป" ซึ่งกันและกัน

สิ่งนี้คือคุณไม่สามารถรู้ข้อยกเว้นทั้งหมดที่อาจเกิดขึ้นได้จากรหัสชิ้นส่วน สิ่งที่คุณสามารถรู้ได้ก็คือหากเป็นการบอกว่าไฟล์ไม่พบข้อยกเว้นคุณสามารถดักจับไฟล์และแจ้งให้ผู้ใช้รับไฟล์ที่ทำหรือยกเลิกฟังก์ชันได้

หากคุณลองจับรอบนั้นไม่ว่าจะมีปัญหาอะไรในรูทีนไฟล์ของคุณ (อ่านอย่างเดียวสิทธิ์ UAC ไม่ใช่ pdf จริงๆ ฯลฯ ) ทุกคนจะหล่นลงในไฟล์ของคุณไม่พบการจับและผู้ใช้ของคุณ กำลังกรีดร้อง "แต่มีอยู่รหัสนี้คืออึ"

ตอนนี้มีสถานการณ์สองสามอย่างที่คุณอาจจับได้ทุกอย่าง แต่ควรเลือกอย่างมีสติ

พวกเขาถูกจับเลิกทำการกระทำบางอย่างในพื้นที่ (เช่นการสร้างหรือล็อกทรัพยากร (เช่นการเปิดไฟล์บนดิสก์เพื่อเขียนเป็นต้น) จากนั้นคุณจะโยนข้อยกเว้นอีกครั้งเพื่อจัดการในระดับที่สูงขึ้น)

คุณไม่สนใจว่าทำไมมันถึงผิดพลาด การพิมพ์เช่น คุณอาจจับได้ตลอดเวลาว่ามีปัญหากับเครื่องพิมพ์ของคุณโปรดแยกแยะออกและอย่าฆ่าแอปพลิเคชันเพราะมัน ในทำนองเดียวกันก็ไร้ประโยชน์หากโค้ดของคุณดำเนินการชุดของงานแยกกันโดยใช้ตารางเวลาบางประเภทคุณจะไม่ต้องการให้ทุกอย่างตายเพราะงานหนึ่งล้มเหลว

หมายเหตุหากคุณทำตามข้างต้นฉันไม่สามารถแนะนำการบันทึกข้อยกเว้นบางประเภทได้เช่นลองใช้ catch end log ให้มากพอ


ฉันจะบอกว่ามันเกี่ยวกับความสมดุล คุณต้องจับข้อยกเว้นให้เร็วพอที่จะสามารถกู้คืนได้และสายพอที่จะรู้ว่าจะไปที่ไหน นั่นเป็นเหตุผลที่การจัดการข้อยกเว้นของ Java ทำให้เกิดความเสียหายดังกล่าวเนื่องจากคุณต้องห่อข้อยกเว้นอีกครั้งในแต่ละขั้นตอนและข้อมูลที่คุณหลวม
dhill

2
+1 สำหรับ "คุณไม่สนใจว่าทำไมจึงผิดพลาด" ฉันใช้มันในหลาย ๆ ที่รอบ ๆ โค้ดบรรทัดเดียวซึ่งฉันแยกวิเคราะห์วันที่ / เวลาจาก URL ไลบรารีการแยกวิเคราะห์วันที่ / เวลาของบุคคลที่สามไม่ได้แสดงรายการข้อยกเว้นทั้งหมดที่สามารถโยนได้ (ฉันพบ OverflowError และ TypeError นอกเหนือจาก ValueError มาตรฐาน แต่อาจมีมากกว่านั้น) และอย่างไรก็ตามฉันไม่สนใจว่าทำไม มีข้อยกเว้นเกิดขึ้นฉันแค่ต้องการส่งข้อความแสดงข้อผิดพลาดที่สมเหตุสมผลกลับไปยังผู้ใช้โดยแจ้งว่ามีบางอย่างผิดปกติกับวันที่ / เวลา
Michael Rodby

3

คุณจะจับเช่น Control-C ด้วยดังนั้นอย่าทำเช่นนั้นเว้นแต่คุณจะ "โยน" อีกครั้ง อย่างไรก็ตามในกรณีนี้คุณควรใช้ "สุดท้าย"


3

มักจะระบุชนิดยกเว้นมีหลายประเภทที่คุณไม่ต้องการที่จะจับเช่นSyntaxError, KeyboardInterrupt, MemoryErrorฯลฯ


2
จะใช้การexcept Exception:หลีกเลี่ยงประเภทข้างต้นที่เราไม่ต้องการจับ?
HorseloverFat

except Exceptionสบายดี.
Ulrich Eckhardt

4
@HorseloverFat: except ExceptionจับSyntaxErrorและMemoryErrorเพราะมันเป็นคลาสพื้นฐานของพวกเขา KeyboardInterrupt, SystemExit(ยกโดยsys.exit()) ไม่ถูกจับ (เป็นคลาสย่อย BaseException ในทันที)
jfs

ดูเหมือนจะไม่เหมาะ - ควรระบุให้แม่นยำกว่านี้
HorseloverFat

3

นี่คือสถานที่ที่ฉันใช้ยกเว้นไม่มีประเภท

  1. การสร้างต้นแบบที่รวดเร็วและสกปรก

นั่นคือการใช้งานหลักในรหัสของฉันสำหรับข้อยกเว้นที่ไม่ได้ตรวจสอบ

  1. ฟังก์ชัน main () ระดับบนสุดที่ฉันบันทึกทุกข้อยกเว้นที่ไม่ถูกจับ

ฉันเพิ่มสิ่งนี้เสมอเพื่อไม่ให้โค้ดการผลิตหกสแต็กเทรซ

  1. ระหว่างชั้นแอปพลิเคชัน

ฉันมีสองวิธีที่จะทำ:

  • วิธีแรกที่ต้องทำ: เมื่อเลเยอร์ระดับที่สูงขึ้นเรียกใช้ฟังก์ชันระดับล่างมันจะตัดการโทรด้วยการพิมพ์ยกเว้นเพื่อจัดการกับข้อยกเว้นระดับล่าง "บนสุด" แต่ฉันเพิ่มคำสั่งยกเว้นทั่วไปเพื่อตรวจจับข้อยกเว้นระดับล่างที่ไม่สามารถจัดการได้ในฟังก์ชันระดับล่าง

ฉันชอบวิธีนี้มากกว่าฉันพบว่าง่ายกว่าในการตรวจจับว่าข้อยกเว้นใดควรถูกจับได้อย่างเหมาะสม: ฉัน "เห็น" ปัญหาได้ดีขึ้นเมื่อมีการบันทึกข้อยกเว้นระดับล่างโดยระดับที่สูงขึ้น

  • วิธีที่สองในการทำ: แต่ละฟังก์ชันระดับบนสุดของเลเยอร์ระดับล่างจะมีโค้ดที่รวมอยู่ในแบบทั่วไปยกเว้นเพื่อจับข้อยกเว้นที่ไม่ได้จัดการทั้งหมดในเลเยอร์นั้น ๆ

เพื่อนร่วมงานบางคนชอบวิธีนี้เนื่องจากรักษาข้อยกเว้นระดับล่างไว้ในฟังก์ชันระดับล่างโดยที่พวกเขา "เป็นของ"


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.