ทำไม“ ยกเว้น: ผ่าน” การฝึกเขียนโปรแกรมที่ไม่ดี?


324

ฉันมักจะเห็นความคิดเห็นเกี่ยวกับคำถาม Stack Overflow อื่น ๆ เกี่ยวกับวิธีการใช้งานที่except: passหมดกำลังใจ ทำไมสิ่งนี้ถึงไม่ดี? บางครั้งฉันก็ไม่สนใจว่าข้อผิดพลาดคืออะไรและฉันต้องการที่จะดำเนินการต่อกับรหัส

try:
    something
except:
    pass

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


1
อย่างน้อยที่สุดฉันขอแนะนำให้คุณบันทึกดังนั้นคุณรู้ว่าคุณกำลังมองข้ามปัญหาอะไร ใช้loggingโมดูลที่ระดับ DEBUG เพื่อหลีกเลี่ยงการสตรีมมิงค์ในการผลิต แต่ให้พร้อมในการพัฒนา
pcurry

คำตอบ:


346

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

คำอธิบายของฉันคือ“ บิต” อีกต่อไป - ดังนั้น tl มันแบ่งลงไปนี้:

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

แต่มาดูรายละเอียดกันดีกว่า:

อย่าจับข้อผิดพลาดใด ๆ

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

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

อย่างไรก็ตามหากเราต้องจับทุกอย่างแล้ว - นอกเหนือจากข้อยกเว้นเหล่านั้นที่เราพร้อมจะกู้คืน - ยังมีโอกาสที่เราจะได้รับข้อยกเว้นที่เราไม่คาดหวังและที่เราไม่สามารถกู้คืนได้แน่นอน หรือไม่ควรกู้คืนจาก

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

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

แต่ก็มีข้อยกเว้นที่อันตรายกว่าซึ่งเราไม่ได้เตรียมไว้ ตัวอย่างเช่นSystemErrorมักจะเป็นสิ่งที่เกิดขึ้นได้ยากและเราไม่สามารถวางแผนได้ มันหมายถึงมีบางสิ่งที่ซับซ้อนมากขึ้นเกิดขึ้นสิ่งที่อาจทำให้เราไม่สามารถดำเนินงานปัจจุบันต่อไปได้

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

พยายามหลีกเลี่ยงการส่งต่อยกเว้นบล็อก

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

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

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

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

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

except: pass

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


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


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

1
วิธีนี้ช่วยให้เข้าใจได้ชัดเจน: พวกมันใช้ผ้าห่มexceptแต่ก็raiseไม่ต้องมีการโต้แย้งใด ๆ เพื่อให้เกิดข้อยกเว้นต่อไป ผมชอบมัน: ianbicking.org/blog/2007/09/re-raising-exceptions.html exceptดูเหมือนจะเป็นข้อยกเว้นที่เป็นของแข็งกฎเกี่ยวกับการไม่ใช้ผ้าห่ม
Gabriel Staples

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

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

262

ปัญหาหลักที่นี่คือมันจะละเว้นและข้อผิดพลาดใด ๆ : ออกจากหน่วยความจำ CPU กำลังเขียนผู้ใช้ต้องการหยุดโปรแกรมต้องการออก Jabberwocky กำลังฆ่าผู้ใช้

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

นั่นเป็นเหตุผลที่คุณควร จำกัด ตัวเองให้เพิกเฉยต่อข้อผิดพลาดเฉพาะบางอย่างเท่านั้น


75

การรันโค้ดหลอกของคุณอย่างแท้จริงไม่ได้ให้ข้อผิดพลาดใด ๆ :

try:
    something
except:
    pass

NameErrorราวกับว่ามันเป็นชิ้นส่วนที่ถูกต้องสมบูรณ์ของรหัสแทนการขว้างปา ฉันหวังว่านี่ไม่ใช่สิ่งที่คุณต้องการ


51

ทำไม“ ยกเว้น: ผ่าน” การฝึกเขียนโปรแกรมที่ไม่ดี?

ทำไมสิ่งนี้ถึงไม่ดี?

try:
    something
except:
    pass

นี้จับทุกข้อยกเว้นที่เป็นไปได้รวมทั้งGeneratorExit, KeyboardInterruptและSystemExit- ซึ่งเป็นข้อยกเว้นที่คุณอาจไม่ได้ตั้งใจจะจับ BaseExceptionมันเป็นเช่นเดียวกับการจับ

try:
    something
except BaseException:
    pass

เก่ารุ่นของเอกสารกล่าวว่า :

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

Python Exception Hierarchy

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

นี่คือลำดับชั้นข้อยกเว้น Python 3 - คุณต้องการที่จะจับพวกมันทั้งหมดหรือไม่:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

อย่าทำอย่างนี้

หากคุณใช้การจัดการข้อยกเว้นในรูปแบบนี้:

try:
    something
except: # don't just do a bare except!
    pass

จากนั้นคุณจะไม่สามารถขัดจังหวะsomethingบล็อกด้วย Ctrl-C โปรแกรมของคุณจะมองข้ามทุกข้อยกเว้นที่เป็นไปได้ภายในtryบล็อคโค้ด

นี่เป็นอีกตัวอย่างที่จะมีพฤติกรรมที่ไม่พึงประสงค์เหมือนกัน:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

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

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

คำอธิบายเพิ่มเติมพร้อมตัวอย่างอื่น

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

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

ด้วยการจัดการข้อผิดพลาดที่แม่นยำยิ่งขึ้นรหัสของคุณจะแข็งแกร่งขึ้น


31
>>> import this

The Zen of Python โดย Tim Peters

สวยดีกว่าน่าเกลียด
ชัดเจนดีกว่าโดยปริยาย
เรียบง่ายดีกว่าซับซ้อน
คอมเพล็กซ์ดีกว่าซับซ้อน
แบนดีกว่าซ้อนกัน
เบาบางดีกว่าหนาแน่น
จำนวนการอ่าน
กรณีพิเศษไม่พิเศษพอที่จะทำลายกฎ
แม้ว่าการปฏิบัติจริงชนะความบริสุทธิ์
ข้อผิดพลาดไม่ควรผ่านไปอย่างเงียบ ๆ
เว้นแต่จะปิดเสียงอย่างชัดเจน
ในการเผชิญกับความกำกวมปฏิเสธสิ่งล่อใจที่จะคาดเดา
ควรมีอย่างน้อยหนึ่งวิธีที่ชัดเจนกว่าที่จะทำ
แม้ว่าวิธีนี้อาจไม่ชัดเจนในตอนแรกเว้นแต่ว่าคุณเป็นชาวดัตช์
ตอนนี้ดีกว่าไม่เคย
แม้ว่าจะไม่เคยดีกว่าที่เหมาะสมในขณะนี้
หากการนำไปปฏิบัตินั้นยากที่จะอธิบายเป็นความคิดที่ไม่ดี
หากการนำไปปฏิบัตินั้นง่ายต่อการอธิบายอาจเป็นความคิดที่ดี
Namespaces เป็นหนึ่งในแนวคิดที่ยอดเยี่ยม - ลองทำสิ่งเหล่านี้ให้มากขึ้น!

ดังนั้นนี่คือความคิดเห็นของฉัน เมื่อใดก็ตามที่คุณพบข้อผิดพลาดคุณควรทำบางสิ่งเพื่อจัดการเช่นเขียนใน logfile หรืออย่างอื่น อย่างน้อยก็แจ้งให้คุณทราบว่าเคยมีข้อผิดพลาด


64
-1 ข้อโต้แย้งจากผู้มีอำนาจไม่ได้อธิบายอะไรเลย อำนาจอาจผิด
Izkata

23
สิ่งที่ @Izkata เขียนและหนึ่งบรรทัดที่อยู่ด้านล่างนั้นหน่วยงานเดียวกันเขียนว่า: "เว้นแต่จะถูกปิดเสียงอย่างชัดเจน" ซึ่งเป็นสิ่งที่ยกเว้น: Pass ทำ
Ofri Raviv

13
@OfriRaviv ไม่มีไม่ได้เป็นข้อผิดพลาดที่ผ่านปริยาย ? อย่างชัดเจนจะต้องมีการตั้งชื่อข้อผิดพลาดที่ควรจะผ่านอย่างเงียบ ๆ นั่นคือการเป็นที่ชัดเจนเกี่ยวกับเรื่องนี้ นี่ไม่ใช่สิ่งที่ยกเว้น: ผ่านไม่ได้
Chelonian

24

คุณควรใช้เวลาอย่างน้อยexcept Exception:เพื่อหลีกเลี่ยงการจับข้อยกเว้นระบบเช่นหรือSystemExit KeyboardInterruptนี่คือลิงค์ไปยังเอกสาร

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


13

ประการแรกมันละเมิดหลักการสองข้อของZen of Python :

  • ชัดเจนดีกว่าโดยปริยาย
  • ข้อผิดพลาดไม่ควรผ่านไปอย่างเงียบ ๆ

หมายความว่าอะไรคือคุณตั้งใจทำผิดพลาดอย่างเงียบ ๆ นอกจากนี้คุณไม่ทราบเหตุการณ์ซึ่งเกิดข้อผิดพลาดอย่างแน่นอนเนื่องจากexcept: passจะตรวจจับข้อยกเว้นใด ๆ

ประการที่สองถ้าเราพยายามแยกออกจาก Zen of Python และพูดอย่างมีสติคุณควรรู้ว่าการใช้งานexcept:passทำให้คุณไม่มีความรู้และการควบคุมในระบบของคุณ กฎของหัวแม่มือคือการยกข้อยกเว้นหากเกิดข้อผิดพลาดและดำเนินการที่เหมาะสม หากคุณไม่ทราบล่วงหน้าสิ่งที่ควรทำอย่างน้อยที่สุดควรบันทึกข้อผิดพลาดที่ใดที่หนึ่ง (และควรเพิ่มข้อยกเว้นใหม่):

try:
    something
except:
    logger.exception('Something happened')

แต่โดยปกติถ้าคุณพยายามที่จะรับข้อยกเว้นใด ๆ คุณอาจทำอะไรผิดพลาด!


2
... ถ้าไม่ได้ปิดเสียงอย่างชัดเจนซึ่งเป็นกรณีของ OP
Hyperboreus

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

2
@Hyperboreus ฉันไม่คิดว่าการจับทั้งหมดและข้อผิดพลาดใด ๆ จะทำให้พวกเขาเงียบลงอย่างชัดเจนนั่นคือคุณไม่รู้ด้วยซ้ำว่าคุณจับอะไร
Alexander Zhukov

13
"เพราะผู้ชายบางคนพูดอย่างนั้น" ไม่ใช่คำตอบของ "ทำไม" คำถาม.
Sebastian Negraszus

12

การexcept:passสร้างจะปิดเงื่อนไขใด ๆ และพิเศษทั้งหมดที่เกิดขึ้นในขณะที่รหัสที่ครอบคลุมในtry:บล็อกกำลังถูกเรียกใช้

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

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


12

มีการระบุเหตุผล # 1 ไว้แล้ว - ซ่อนข้อผิดพลาดที่คุณไม่คาดคิด

(# 2) - ทำให้โค้ดของคุณยากสำหรับผู้อื่นในการอ่านและทำความเข้าใจหากคุณจับ FileNotFoundException เมื่อคุณพยายามอ่านไฟล์มันก็ค่อนข้างชัดเจนสำหรับนักพัฒนาคนอื่น ๆ ว่าฟังก์ชั่นใดที่บล็อก 'catch' ควรมี หากคุณไม่ได้ระบุข้อยกเว้นคุณต้องแสดงความคิดเห็นเพิ่มเติมเพื่ออธิบายสิ่งที่บล็อกควรทำ

(# 3) - มันแสดงให้เห็นถึงการเขียนโปรแกรมขี้เกียจ หากคุณใช้ try / catch แบบทั่วไปแสดงว่าคุณไม่เข้าใจข้อผิดพลาดที่อาจเกิดขึ้นในโปรแกรมของคุณหรือคุณไม่ทราบว่ามีข้อยกเว้นใดบ้างใน Python การตรวจจับข้อผิดพลาดเฉพาะแสดงว่าคุณเข้าใจทั้งโปรแกรมและช่วงของข้อผิดพลาดที่ Python พ่น นี่มีแนวโน้มที่จะทำให้นักพัฒนาซอฟต์แวร์และผู้ตรวจทานโค้ดเชื่อถืองานของคุณมากขึ้น


12

ดังนั้นรหัสนี้ผลิตผลอะไร

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

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


5
เอ้อขอบคุณผู้คนที่ 'แก้ไข' สิ่งนี้ แต่โปรดอย่า - มันผิดโดยเจตนาในแง่ 'คำถามสัมภาษณ์' อาจเป็นเรื่องที่ลึกซึ้งกว่าที่ปรากฏครั้งแรก - ลองใช้ดู ประเด็นของฉันคือการยกเว้นข้อผิดพลาด 'ทั้งหมด' โดยเฉพาะอย่างยิ่งใน Python ทำให้การดีบักทำได้ยากแม้จะอยู่ในบรรทัดรหัสโหลจำนวนเล็กน้อย
Ian Harvey

11

โดยทั่วไปคุณสามารถจำแนกข้อผิดพลาด / ข้อยกเว้นในหนึ่งในสามหมวดหมู่ :

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

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

  • ภายนอก : คุณสามารถคาดหวังว่าข้อผิดพลาดในสถานการณ์พิเศษเช่นไฟล์ไม่พบหรือการเชื่อมต่อยกเลิก คุณควรจัดการข้อผิดพลาดเหล่านี้อย่างชัดเจนและเฉพาะสิ่งเหล่านี้

ในทุกกรณีexcept: passจะทำให้โปรแกรมของคุณอยู่ในสถานะที่ไม่รู้จักซึ่งจะทำให้เกิดความเสียหายมากขึ้น


6

พูดง่ายๆคือถ้ามีการโยนข้อยกเว้นหรือข้อผิดพลาดมีบางอย่างผิดปกติ มันอาจจะไม่ใช่สิ่งที่ผิดปกติ แต่การสร้างการขว้างปาและการจับข้อผิดพลาดและข้อยกเว้นเพื่อประโยชน์ในการใช้คำสั่ง goto นั้นไม่ใช่ความคิดที่ดีและทำได้ยาก 99% ของเวลามีปัญหาเกิดขึ้นที่ไหนซักแห่ง

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

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

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

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


6

ในความคิดของฉันผิดมีเหตุผลที่จะปรากฏว่าเสียงของฉันโง่ แต่นั่นคือวิธีที่มันเป็น การเขียนโปรแกรมที่ดีจะทำให้เกิดข้อผิดพลาดเมื่อคุณต้องจัดการกับมันเท่านั้น นอกจากนี้เมื่อฉันอ่านบางเวลาที่ผ่านมา "คำสั่ง pass เป็นคำแถลงที่แสดงรหัสจะถูกแทรกในภายหลัง" ดังนั้นหากคุณต้องการให้ว่างยกเว้น statement ให้ทำ แต่สำหรับโปรแกรมที่ดีจะมี เป็นส่วนหนึ่งที่ขาดหายไป เพราะคุณไม่ได้จัดการกับสิ่งที่คุณควรมี การปรากฏข้อยกเว้นให้โอกาสคุณในการแก้ไขข้อมูลอินพุตหรือเปลี่ยนโครงสร้างข้อมูลของคุณดังนั้นข้อยกเว้นเหล่านี้จะไม่เกิดขึ้นอีก (แต่ในกรณีส่วนใหญ่ (ข้อยกเว้นเครือข่าย, ข้อยกเว้นอินพุตทั่วไป) ข้อยกเว้นบ่งชี้ว่าส่วนถัดไปของโปรแกรม ตัวอย่างเช่น NetworkException สามารถระบุการเชื่อมต่อเครือข่ายที่ใช้งานไม่ได้และโปรแกรมไม่สามารถส่ง / รับข้อมูลในขั้นตอนโปรแกรมถัดไป

แต่การใช้ Pass Block สำหรับ Execution-Block เพียงอันเดียวนั้นถูกต้องเนื่องจากคุณยังคงแตกต่างกันระหว่างประเภทของข้อยกเว้นดังนั้นหากคุณใส่บล็อกข้อยกเว้นทั้งหมดไว้ในที่เดียวจะไม่ว่างเปล่า:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

สามารถเขียนใหม่ได้ด้วยวิธีนี้:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

ดังนั้นแม้กระทั่งบล็อกยกเว้นหลายรายการที่มีคำสั่ง pass อาจส่งผลให้เกิดโค้ดซึ่งโครงสร้างจะจัดการกับข้อยกเว้นชนิดพิเศษได้


4

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

จากทั้งหมดที่กล่าวมาอย่าใช้การฝึกเขียนโปรแกรมใด ๆ เป็นสิ่งสำคัญยิ่ง นี่มันงี่เง่า จะมีเวลาและสถานที่ที่จะทำการบล็อก

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


1
กรณี "goto" เป็นโวหารและเป็นเรื่องของความคิดเห็นในขณะที่ "ยกเว้น: ผ่าน" มักเป็นความจริงที่ผิด สมมติว่าถ้ามีคนทำตัวอย่างเช่น "kill -TERM" กระบวนการของคุณที่จุดนั้นก็ควรละเว้นมัน อย่างน้อยที่สุดนั่นเป็นพฤติกรรมที่ไม่ดี
คะแนน _ ต่ำกว่า

1
@Score_Under ยังมีกรณีที่เหมาะสมที่จะใช้ ตัวอย่างเช่นเมื่อฟังก์ชั่นที่คุณกำลังเรียกนั้นเป็นส่วนเสริมซึ่งไม่ทราบที่มา / ผู้เขียนจะไม่ส่งผลกระทบต่อฟังก์ชันการทำงานหลัก แต่หากเกิดปัญหาอาจทำให้เกิดปัญหา ฉันรู้ว่าคุณจะโต้แย้งว่าการโทรดังกล่าวควรได้รับการวิจัยและวิเคราะห์อย่างเหมาะสม แต่ในชีวิตจริงมันเป็นไปไม่ได้เสมอไป
กาแลตส์

ยังคงถ้าฉันต้องการที่จะยุติกระบวนการของคุณฆ่า -9 ไม่ควรเป็นตัวเลือกที่เชื่อถือได้เท่านั้น
คะแนน _ ต่ำกว่า

2

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


2

เนื่องจากยังไม่ได้รับการกล่าวถึงจึงควรใช้สไตล์ที่ดีกว่าcontextlib.suppress:

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

โปรดสังเกตว่าในตัวอย่างที่มีให้สถานะโปรแกรมยังคงเหมือนเดิมไม่ว่าจะเกิดข้อยกเว้นขึ้นหรือไม่ กล่าวคือจะsomefile.tmpไม่มีอยู่จริงเสมอ

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