ตรวจสอบการจัดการข้อยกเว้นครั้งแรกหรือไม่


88

ฉันกำลังทำงานกับหนังสือ"Head First Python" (เป็นภาษาของฉันที่จะเรียนรู้ในปีนี้) และฉันได้ไปยังส่วนที่พวกเขาโต้เถียงเกี่ยวกับเทคนิคสองรหัส: การ
ตรวจสอบการจัดการข้อยกเว้นข้อแรก

นี่คือตัวอย่างของรหัสไพ ธ อน:

# Checking First
for eachLine in open("../../data/sketch.txt"):
    if eachLine.find(":") != -1:
        (role, lineSpoken) = eachLine.split(":",1)
        print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())

# Exception handling        
for eachLine in open("../../data/sketch.txt"):
    try:
        (role, lineSpoken) = eachLine.split(":",1)
        print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
    except:
        pass

ตัวอย่างแรกเกี่ยวข้องโดยตรงกับปัญหาใน.splitฟังก์ชัน คนที่สองเพียงแค่ให้ตัวจัดการข้อยกเว้นจัดการกับมัน (และละเว้นปัญหา)

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

ข้อใดในสองข้อนี้ที่ถือว่าเป็นการปฏิบัติที่ดีกว่า?


12
ส่วนในหนังสือนั้นไม่ฉลาด หากคุณอยู่ในวงและคุณกำลังโยนข้อยกเว้นซ้ำไปซ้ำมามันมีราคาแพงมาก ฉันพยายามที่จะร่างจุดที่ดีเมื่อทำสิ่งนี้
Jason Sebring

9
อย่าตกหลุมพราง "ไฟล์ที่มีอยู่แล้ว" กับดัก มีไฟล์อยู่แล้ว! = มีการเข้าถึงไฟล์หรือว่าจะมีอยู่ใน 10 มิลลิวินาทีก็จะได้รับการโทรแบบเปิดไฟล์ของฉัน ฯลฯblogs.msdn.com/b/jaredpar/archive/2009/04/27/ ......
Billy ONeal

11
ข้อยกเว้นมีความคิดแตกต่างกันใน Python กว่าในภาษาอื่น ยกตัวอย่างเช่นวิธีการย้ำผ่านคอลเลกชันคือการเรียก. Next () บนมันจนกว่ามันจะโยนข้อยกเว้น
WuHoUnited

4
@ emeraldcode.com ไม่เป็นความจริงทั้งหมดเกี่ยวกับ Python ฉันไม่ทราบรายละเอียดเฉพาะ แต่ภาษานั้นถูกสร้างขึ้นในกระบวนทัศน์นั้นดังนั้นการยกเว้นการขว้างปาไม่ได้มีค่าใช้จ่ายสูงเท่ากับในภาษาอื่น
Izkata

ที่กล่าวว่าสำหรับตัวอย่างนี้ฉันจะใช้คำสั่งยาม: if -1 == eachLine.find(":"): continueจากนั้นส่วนที่เหลือของวงจะไม่เยื้อง
Izkata

คำตอบ:


68

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

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

ที่หัวใจของการโต้แย้งเป็นคำพูดต่อไปนี้:

เหตุผลก็คือฉันคิดว่าข้อยกเว้นนั้นไม่ดีไปกว่า "goto's" ซึ่งถือว่าอันตรายมาตั้งแต่ทศวรรษ 1960 โดยที่พวกมันสร้างการกระโดดแบบฉับพลันจากจุดหนึ่งไปยังอีกจุดหนึ่ง ในความเป็นจริงพวกเขาแย่กว่าของ goto:

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

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

โดยส่วนตัวแล้วฉันจะโยนข้อยกเว้นเมื่อรหัสของฉันไม่สามารถทำสิ่งที่จะทำสัญญา ฉันมักจะใช้ลอง / จับเมื่อฉันกำลังจะจัดการกับบางสิ่งที่อยู่นอกขอบเขตกระบวนการของฉันเช่นการโทร SOAP, การโทรฐานข้อมูล, ไฟล์ IO หรือการโทรของระบบ มิฉะนั้นฉันพยายามที่จะรหัสป้องกัน มันไม่ใช่กฎที่ยากและรวดเร็ว แต่เป็นการฝึกฝนทั่วไป

สกอตต์ Hanselman ยังเขียนเกี่ยวกับข้อยกเว้นใน .NET ที่นี่ ในบทความนี้เขาอธิบายกฎหลายข้อเกี่ยวกับข้อยกเว้น ของโปรด?

คุณไม่ควรโยนข้อยกเว้นสำหรับสิ่งที่เกิดขึ้นตลอดเวลา จากนั้นพวกเขาจะเป็น "นักบวช"


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

2
คำตอบที่ดี หมายเหตุแม้ว่าข้อยกเว้นจะมีประสิทธิภาพสูงในแพลตฟอร์มส่วนใหญ่ อย่างไรก็ตามตามที่คุณจะได้จดบันทึกไว้กับความคิดเห็นของฉันเกี่ยวกับคำตอบอื่น ๆ การปฏิบัติไม่ใช่การพิจารณาในกรณีของการตัดสินใจกฎครอบคลุมสำหรับวิธีการประมวลผลบางสิ่งบางอย่าง
mattnz

1
อ้างจาก Scott Hanselman อธิบายทัศนคติ. สุทธิที่มีต่อข้อยกเว้นมากกว่า "มากเกินไป" การพูดถึงประสิทธิภาพบ่อยครั้ง แต่ข้อโต้แย้งที่แท้จริงคือสิ่งที่ตรงกันข้ามว่าทำไมคุณควรใช้ข้อยกเว้น - ทำให้โค้ดยากต่อการเข้าใจและจัดการเมื่อเงื่อนไขปกติส่งผลให้เกิดข้อยกเว้น สำหรับโจเอลนั้นจุดที่ 1 นั้นจริง ๆ แล้วเป็นบวก (ที่มองไม่เห็นหมายความว่าโค้ดแสดงสิ่งที่มันทำไม่ใช่สิ่งที่ไม่ได้ทำ) และจุดที่ไม่เกี่ยวข้อง 2 (คุณอยู่ในสถานะไม่สอดคล้องกันหรือไม่ควรมีข้อยกเว้น) . ถึงกระนั้น +1 สำหรับ "ไม่สามารถทำสิ่งที่ขอให้ทำ"
jmoreno

5
ในขณะที่คำตอบนี้ใช้ได้สำหรับ. Net มันไม่ได้เป็นpythonicมากดังนั้นเนื่องจากนี่เป็นคำถาม python ฉันไม่สามารถดูได้ว่าทำไมคำตอบของIvcจึงไม่ได้รับการโหวตมากขึ้น
Mark Booth

2
@IanGoldby: ไม่ การจัดการข้อยกเว้นอธิบายได้ดีกว่าเป็นการกู้คืนข้อยกเว้น หากคุณไม่สามารถกู้คืนจากข้อยกเว้นได้คุณอาจไม่ควรมีรหัสการจัดการข้อยกเว้นใด ๆ ถ้าวิธี A เรียกวิธี B ซึ่งเรียก C และ C พ่นแล้วน่าจะ EITHER A หรือ B ควรกู้คืนไม่ใช่ทั้งสองอย่าง การตัดสินใจ "ถ้าฉันทำ X ไม่ได้ฉันจะทำ Y" ควรหลีกเลี่ยงถ้า Y ต้องการให้คนอื่นทำงานให้เสร็จ หากคุณไม่สามารถทำงานให้เสร็จสิ้นสิ่งที่เหลืออยู่คือการล้างข้อมูลและการบันทึก การล้างข้อมูลใน. net ควรเป็นแบบอัตโนมัติการบันทึกควรเป็นแบบรวมศูนย์
jmoreno

78

โดยเฉพาะอย่างยิ่งในงูหลามก็มักจะถือว่าเป็นวิธีที่ดีกว่าที่จะจับข้อยกเว้น มันมีแนวโน้มที่จะถูกเรียกว่าง่ายกว่าที่จะขอการให้อภัยมากกว่าการอนุญาต (EAFP) เปรียบเทียบกับ Look Before You Leap (LBYL) มีหลายกรณีที่ LBYL จะให้ข้อบกพร่องเล็กน้อยในบางกรณี

อย่างไรก็ตามโปรดระวังข้อความที่เปลือยเปล่าและexcept:ข้อความเกินยกเว้นข้อความเนื่องจากพวกเขาสามารถปกปิดข้อผิดพลาดได้ด้วย - สิ่งนี้จะดีกว่า:

for eachLine in open("../../data/sketch.txt"):
    try:
        role, lineSpoken = eachLine.split(":",1)
    except ValueError:
        pass
    else:
        print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())

8
ในฐานะที่เป็นโปรแกรมเมอร์ NET ฉันก็ประจบประแจง แต่แล้วอีกครั้งคุณคนทำทุกอย่างแปลก ๆ :)
ฟิล

นี่เป็นสิ่งที่น่าหงุดหงิดอย่างมาก (ไม่ได้ตั้งใจจะเล่น) เมื่อ API ไม่สอดคล้องกับข้อยกเว้นที่จะเกิดขึ้นภายใต้สถานการณ์ใดหรือเมื่อเกิดความล้มเหลวหลายประเภทที่แตกต่างกันภายใต้ประเภทข้อยกเว้นเดียวกัน
แจ็ค

ดังนั้นคุณจะใช้กลไกเดียวกันสำหรับข้อผิดพลาดที่ไม่คาดคิดและค่าการส่งคืนชนิดที่คาดหวัง นั่นเป็นเรื่องที่ยอดเยี่ยมเช่นเดียวกับการใช้ 0 เป็นตัวเลขบูลเท็จและตัวชี้ที่ไม่ถูกต้องที่จะออกจากกระบวนการของคุณด้วยรหัสทางออก 128 + SIGSEGV เพราะสะดวกคุณไม่ต้องการสิ่งต่าง ๆ ในตอนนี้ เหมือน Spork! หรือรองเท้าที่มีนิ้วเท้า ...
องค์รักษ์

2
@yeoman เมื่อมีการโยนข้อยกเว้นเป็นคำถามที่แตกต่างกันอันนี้เป็นเรื่องเกี่ยวกับการใช้try/ exceptมากกว่าการตั้งค่าเงื่อนไขสำหรับ "คือต่อไปนี้มีแนวโน้มที่จะโยนข้อยกเว้น" และปฏิบัติหลามแน่นอนจะชอบอดีต ไม่เจ็บที่วิธีการนั้น (อาจ) มีประสิทธิภาพมากขึ้นที่นี่เนื่องจากในกรณีที่การแบ่งสำเร็จคุณจะเดินเพียงครั้งเดียว ว่าsplitควรจะมีข้อยกเว้นที่นี่ฉันจะบอกว่ามันควร - กฎทั่วไปอย่างหนึ่งคือคุณควรโยนเมื่อคุณไม่สามารถทำสิ่งที่ชื่อของคุณพูดและคุณไม่สามารถแยกตัวคั่นที่ขาดหายไป
lvc

ฉันไม่พบว่ามันแย่หรือช้าหรือแย่มากโดยเฉพาะอย่างยิ่งเมื่อมีข้อผิดพลาดเกิดขึ้น ตอบฉันชอบ Python จริงๆ มันตลกดีที่บางครั้งมันก็ไม่แสดงรสชาติเลยดังที่ C บอกไว้ว่าใช้ตัวเลขศูนย์ Spork และรองเท้าโปรดตลอดกาลด้วยนิ้วเท้าของ Randall Munroe :) Plus เมื่อฉันอยู่ใน Python และ API บอกว่านี่คือ วิธีที่จะทำฉันจะไปหามัน :) การตรวจสอบเงื่อนไขล่วงหน้าแน่นอนว่าไม่ควรคิดเพราะการทำงานพร้อมกัน, coroutines หรือหนึ่งในสิ่งที่ถูกเพิ่มเข้ามาไกลออกไป ...
Yeoman

27

แนวทางปฏิบัติ

คุณควรจะป้องกัน แต่จนถึงจุดหนึ่ง คุณควรเขียนการจัดการข้อยกเว้น แต่ตรงประเด็น ฉันจะใช้การเขียนโปรแกรมเว็บเป็นตัวอย่างเพราะนี่คือที่ที่ฉันอยู่

  1. สมมติว่าอินพุตของผู้ใช้ทั้งหมดไม่ดีและเขียนป้องกันไปยังจุดตรวจสอบชนิดข้อมูลการตรวจสอบรูปแบบและการฉีดที่เป็นอันตรายเท่านั้น การตั้งโปรแกรมป้องกันควรเป็นสิ่งที่อาจเกิดขึ้นบ่อยครั้งมากซึ่งคุณไม่สามารถควบคุมได้
  2. เขียนข้อยกเว้นการจัดการบริการเครือข่ายที่อาจล้มเหลวในเวลาและจัดการอย่างสง่างามสำหรับข้อเสนอแนะของผู้ใช้ ควรใช้การเขียนโปรแกรมข้อยกเว้นสำหรับสิ่งที่อยู่ในเครือข่ายซึ่งอาจล้มเหลวเป็นครั้งคราว แต่โดยปกติจะแข็งและคุณต้องทำให้โปรแกรมทำงานต่อไป
  3. อย่ากังวลที่จะเขียนเชิงป้องกันภายในแอปพลิเคชันของคุณหลังจากตรวจสอบข้อมูลอินพุตแล้ว มันเป็นการเสียเวลาและทำให้แอปของคุณบวม ปล่อยให้มันระเบิดเพราะบางสิ่งบางอย่างหายากมากที่ไม่คุ้มค่ากับการจัดการหรือหมายความว่าคุณต้องดูขั้นตอนที่ 1 และ 2 อย่างระมัดระวังมากขึ้น
  4. อย่าเขียนข้อยกเว้นการจัดการภายในรหัสหลักของคุณที่ไม่ได้ขึ้นอยู่กับอุปกรณ์เครือข่าย การทำเช่นนั้นคือการเขียนโปรแกรมที่ไม่ดีและเสียค่าใช้จ่ายต่อประสิทธิภาพ ตัวอย่างเช่นการเขียนลองจับในกรณีที่ไม่มีขอบเขตในลูปหมายความว่าคุณไม่ได้ตั้งโปรแกรมลูปอย่างถูกต้องตั้งแต่แรก
  5. ให้ทุกอย่างได้รับการจัดการโดยการบันทึกข้อผิดพลาดกลางที่จับข้อยกเว้นในที่เดียวหลังจากทำตามขั้นตอนข้างต้น คุณไม่สามารถจับทุกกรณีที่ขอบเนื่องจากอาจไม่มีที่สิ้นสุดคุณจะต้องเขียนโค้ดที่จัดการการดำเนินการที่คาดหวัง นั่นเป็นเหตุผลที่คุณใช้การจัดการข้อผิดพลาดกลางเป็นทางเลือกสุดท้าย
  6. TDD เป็นสิ่งที่ดีเพราะในทางใดทางหนึ่งที่ดึงดูดความสนใจให้คุณได้โดยไม่มีการขยายตัวหมายถึงการรับประกันการทำงานปกติ
  7. คะแนนโบนัสคือการใช้เครื่องมือครอบคลุมรหัสเช่นอิสตันบูลเป็นเครื่องมือที่ดีสำหรับโหนดเนื่องจากสิ่งนี้แสดงให้คุณเห็นว่าคุณไม่ได้ทดสอบ
  8. ข้อแม้ทั้งหมดนี้เป็นข้อยกเว้นสำหรับนักพัฒนาที่เป็นมิตร ตัวอย่างเช่นภาษาจะโยนหากคุณใช้ไวยากรณ์ผิดและอธิบายว่าทำไม ดังนั้นยูทิลิตี้ไลบรารี่ของคุณควรเป็นรหัสของคุณหรือไม่

นี่คือจากประสบการณ์การทำงานในสถานการณ์ทีมขนาดใหญ่

การเปรียบเทียบ

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


4
ปัญหาเกี่ยวกับจุดที่ 3 คือมันถือว่าโปรแกรมและโปรแกรมเมอร์ทำงานบนมันสมบูรณ์แบบ มันไม่ใช่โปรแกรมที่ดีที่สุดที่จะป้องกันสิ่งเหล่านี้ไว้ในใจ จำนวนเงินที่เห็นได้ชัดเจน ณ จุดเชื่อมต่อหลักสามารถทำให้ซอฟต์แวร์มีความน่าเชื่อถือมากกว่า "หากมีการตรวจสอบอินพุตที่สมบูรณ์แบบทุกอย่าง"
mattnz

นั่นคือการทดสอบสำหรับ
Jason Sebring

3
การทดสอบนั้นไม่ใช่เรื่องง่าย ฉันยังไม่ได้เห็นชุดทดสอบที่มีรหัส 100% และความคุ้มครอง "สิ่งแวดล้อม"
Marjan Venema

1
@emeraldcode: คุณต้องการงานกับฉันฉันชอบที่จะมีใครบางคนในทีมที่มักจะมีข้อยกเว้นการทดสอบการเปลี่ยนแปลงของทุกกรณีขอบซอฟต์แวร์จะดำเนินการทุกครั้ง จะต้องรู้ดีด้วย abosoluite แน่นอนว่ารหัสของคุณผ่านการทดสอบอย่างสมบูรณ์
mattnz

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

15

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

ฉันคิดว่าข้อความนี้เป็นจริงเฉพาะในสถานการณ์ที่เฉพาะเจาะจงมาก - ที่คุณไม่สนใจว่าผลลัพธ์ถูกต้อง

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

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

ใช้วิธีการยกเว้นถ้าคุณเห็นValueErrorข้อยกเว้นคุณข้ามบรรทัด การใช้วิธีการที่ไม่ใช่ข้อยกเว้นแบบดั้งเดิมคุณจะนับจำนวนค่าที่ส่งคืนจากsplitและถ้าน้อยกว่า 2 คุณจะข้ามบรรทัด คุณควรรู้สึกปลอดภัยมากขึ้นด้วยวิธีการยกเว้นเนื่องจากคุณอาจลืม "ข้อผิดพลาด" สถานการณ์อื่น ๆ ในการตรวจสอบข้อผิดพลาดแบบดั้งเดิมของคุณและexcept ValueErrorจะจับพวกเขาให้คุณ?

ขึ้นอยู่กับลักษณะของโปรแกรมของคุณ

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

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

คุณอาจคิดว่าวิธีเดียวที่คุณสามารถดูValueErrorในรหัสนี้คือถ้าsplitส่งคืนค่าเดียวเท่านั้น (แทนที่จะเป็นสองค่า) แต่ถ้าหากprintคำสั่งของคุณเริ่มใช้นิพจน์ที่เพิ่มขึ้นValueErrorภายใต้เงื่อนไขบางประการ สิ่งนี้จะทำให้คุณข้ามบางบรรทัดไม่ได้เพราะพวกเขาพลาด:แต่เป็นเพราะprintพวกเขาล้มเหลว นี่คือตัวอย่างของข้อผิดพลาดที่ละเอียดอ่อนที่ฉันอ้างถึงก่อนหน้านี้ - คุณจะไม่สังเกตเห็นอะไรเลยเพียงแค่สูญเสียบางบรรทัด

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

สำหรับผลกระทบด้านประสิทธิภาพของการใช้ข้อยกเว้นนั้นเป็นเรื่องเล็กน้อย (เป็น Python) ยกเว้นว่ามีข้อยกเว้นเกิดขึ้นบ่อยครั้ง

หากคุณใช้ข้อยกเว้นเพื่อจัดการกับเงื่อนไขที่เกิดขึ้นเป็นประจำคุณอาจจ่ายค่าประสิทธิภาพจำนวนมากในบางกรณี ตัวอย่างเช่นสมมติว่าคุณดำเนินการคำสั่งบางอย่างจากระยะไกล คุณสามารถตรวจสอบว่าข้อความคำสั่งของคุณผ่านการตรวจสอบขั้นต่ำอย่างน้อยที่สุด (เช่นไวยากรณ์) หรือคุณอาจรอให้มีการยกข้อยกเว้น (ซึ่งจะเกิดขึ้นหลังจากรีโมตเซิร์ฟเวอร์แยกวิเคราะห์คำสั่งของคุณและพบปัญหากับมัน) เห็นได้ชัดว่าอดีตคือคำสั่งของขนาดได้เร็วขึ้น อีกตัวอย่างง่ายๆ: คุณสามารถตรวจสอบว่าตัวเลขเป็นศูนย์ ~ 10 ครั้งเร็วกว่าการพยายามดำเนินการหารแล้วตรวจจับข้อยกเว้น ZeroDivisionError

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

หมายเหตุ: ฉันคิดว่าคุณจะใช้except ValueErrorแทนที่จะเป็นเพียงแค่except; ดังที่คนอื่น ๆ ชี้ให้เห็นและอย่างที่หนังสือเล่มนี้พูดในสองสามหน้าคุณไม่ควรใช้ความโล่งexceptอก

ทราบอีก: วิธีการที่ไม่ใช่ข้อยกเว้นที่เหมาะสมคือการนับจำนวนของค่าที่ส่งกลับโดยแทนที่จะค้นหาsplit :หลังช้าเกินไปเนื่องจากมันทำซ้ำงานที่ทำโดยsplitและอาจเกือบสองเท่าของเวลาดำเนินการ


6

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


2

ใช้สิ่งที่เคยทำงานได้ดีค่ะ

  • ภาษาการเขียนโปรแกรมที่คุณเลือกในแง่ของการอ่านรหัสและประสิทธิภาพ
  • ทีมของคุณและชุดของข้อตกลงรหัสที่ตกลงกันไว้

ทั้งการจัดการข้อยกเว้นและการตั้งโปรแกรมการป้องกันเป็นวิธีที่แตกต่างของการแสดงเจตนาเดียวกัน


0

TBH ไม่สำคัญว่าคุณใช้try/exceptช่างหรือifตรวจสอบใบแจ้งยอด คุณมักจะเห็นทั้ง EAFP และ LBYL ในพื้นฐานของ Python ส่วนใหญ่โดยที่ EAFP นั้นจะเป็นเรื่องธรรมดามากขึ้นเล็กน้อย บางครั้ง EAFP คืออ่านได้มากขึ้น / สำนวน แต่ในกรณีนี้โดยเฉพาะอย่างยิ่งผมคิดว่ามันปรับทางใดทางหนึ่ง

อย่างไรก็ตาม ...

ฉันจะระมัดระวังใช้การอ้างอิงปัจจุบันของคุณ สองสามปัญหาที่จ้องมองด้วยรหัสของพวกเขา:

  1. ตัวอธิบายไฟล์รั่วไหลออกมา CPython เวอร์ชั่นใหม่ ( ล่าม Python ที่เฉพาะเจาะจง ) จะปิดลงจริง ๆ เนื่องจากเป็นวัตถุที่ไม่ระบุชื่อซึ่งอยู่ในขอบเขตระหว่างลูปเท่านั้น (gc จะทำหน้าที่ดักจับมันหลังจากลูป) อย่างไรก็ตามล่ามอื่น ๆไม่มีการรับประกันนี้ พวกเขาอาจรั่วไหลตัวบ่งชี้ทันที คุณมักจะต้องการใช้withสำนวนเมื่ออ่านไฟล์ใน Python: มีข้อยกเว้นน้อยมาก นี่ไม่ใช่หนึ่งในนั้น
  2. การจัดการข้อยกเว้นของโปเกมอนนั้นขมวดคิ้วเนื่องจากมันปกปิดข้อผิดพลาด (เช่นexceptข้อความเปล่าที่ไม่ได้รับการยกเว้น)
  3. Nit: คุณไม่จำเป็นต้องมี parens ในการเปิด tuple ทำได้แค่ไหนrole, lineSpoken = eachLine.split(":",1)

Ivc มีคำตอบที่ดีเกี่ยวกับเรื่องนี้และ EAFP แต่ก็มีการรั่วไหลของคำอธิบาย

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

In [33]: def lbyl(lines):
    ...:     for line in lines:
    ...:         if line.find(":") != -1:
    ...:             # Nuke the parens, do tuple unpacking like an idiomatic Python dev.
    ...:             role, lineSpoken = line.split(":",1)
    ...:             # no print, since output is obnoxiously long with %timeit
    ...:

In [34]: def eafp(lines):
    ...:     for line in lines:
    ...:         try:
    ...:             # Nuke the parens, do tuple unpacking like an idiomatic Python dev.
    ...:             role, lineSpoken = eachLine.split(":",1)
    ...:             # no print, since output is obnoxiously long with %timeit
    ...:         except:
    ...:             pass
    ...:

In [35]: lines = ["abc:def", "onetwothree", "xyz:hij"]

In [36]: %timeit lbyl(lines)
100000 loops, best of 3: 1.96 µs per loop

In [37]: %timeit eafp(lines)
100000 loops, best of 3: 4.02 µs per loop

In [38]: lines = ["a"*100000 + ":" + "b", "onetwothree", "abconetwothree"*100]

In [39]: %timeit lbyl(lines)
10000 loops, best of 3: 119 µs per loop

In [40]: %timeit eafp(lines)
100000 loops, best of 3: 4.2 µs per loop

-4

การจัดการข้อยกเว้นโดยทั่วไปน่าจะเหมาะสมกว่าสำหรับภาษา OOP

ประเด็นที่สองคือประสิทธิภาพเนื่องจากคุณไม่ต้องดำเนินeachLine.findการทุกบรรทัด


7
-1: ประสิทธิภาพเป็นเหตุผลที่แย่มากสำหรับกฎแบบครอบคลุม
mattnz

3
ไม่ข้อยกเว้นไม่เกี่ยวข้องกับ OOP อย่างสมบูรณ์
Pubby

-6

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


7
ยัง anotehr -1 สำหรับความกังวลเกี่ยวกับประสิทธิภาพการทำงานมากกว่าความพร้อมการบำรุงรักษา bla bla bla ประสิทธิภาพไม่ใช่เหตุผล
mattnz

ฉันขอทราบได้ไหมว่าเพราะเหตุใดคุณจึงพยายามกระจาย -1s โดยไม่อธิบาย? การตั้งโปรแกรมการป้องกันหมายถึงรหัสของบรรทัดที่มากขึ้นซึ่งหมายถึงประสิทธิภาพที่ต่ำลง ใครสนใจที่จะอธิบายก่อนที่จะยิงลงคะแนน?
Manoj

3
@Manoj: หากคุณไม่ได้วัดด้วย profiler และพบว่าโค้ดบล็อกนั้นช้าจนไม่อาจยอมรับได้โค้ดสำหรับความสามารถในการอ่านและการบำรุงรักษาได้ดีกว่าประสิทธิภาพ
Daenyth

สิ่งที่ @Manoj กล่าวพร้อมกับเพิ่มรหัสที่น้อยลงในระดับสากลหมายถึงทำงานน้อยลงเมื่อทำการดีบักและบำรุงรักษา เวลาในการพัฒนาสำหรับสิ่งที่น้อยกว่ารหัสที่สมบูรณ์แบบนั้นสูงมาก ฉันกำลังสมมติว่า (เช่นฉัน) คุณไม่ได้เขียนรหัสที่สมบูรณ์แบบให้อภัยฉันถ้าฉันผิด
mattnz

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