เป็นวิธีปฏิบัติที่ดีในการใช้ try-exception-else ใน Python หรือไม่?


439

ฉันเห็นบล็อกเป็นครั้งคราวใน Python

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

อะไรคือเหตุผลที่ทำให้การลองยกเว้นอื่นมีอยู่

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

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

ปกติฉันจัดการข้อยกเว้นเป็น:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

หรือถ้าฉันไม่ต้องการคืนสิ่งใดจริงๆหากมีข้อยกเว้นเกิดขึ้น:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

คำตอบ:


666

"ฉันไม่รู้ว่ามันไม่รู้หรือเปล่า แต่ฉันไม่ชอบการเขียนโปรแกรมแบบนั้นเพราะใช้ข้อยกเว้นในการควบคุมการไหล"

ในโลกหลามการใช้ข้อยกเว้นสำหรับการควบคุมการไหลเป็นเรื่องปกติและปกติ

แม้แต่ผู้พัฒนาคอร์ Python ก็ยังใช้ข้อยกเว้นสำหรับการควบคุมการไหลและรูปแบบนั้นถูกทำให้เป็นภาษาอย่างหนัก (เช่นโพรโทคอล iterator ใช้StopIterationในการเลิกลูปสัญญาณ

นอกจากนี้รูปแบบลองยกเว้นจะถูกนำมาใช้เพื่อป้องกันไม่ให้มีการแข่งขันสภาพธรรมชาติที่มีอยู่ในบางส่วนของ"มองก่อนจะก้าวกระโดด"โครงสร้าง ตัวอย่างเช่นการทดสอบos.path.exists ให้ผลลัพธ์ในข้อมูลที่อาจล้าสมัยตามเวลาที่คุณใช้ ในทำนองเดียวกันQueue.fullส่งกลับข้อมูลที่อาจจะเก่า ลักษณะลองยกเว้นจะสร้างรหัสที่เชื่อถือได้มากขึ้นในกรณีเหล่านี้

"มันเป็นความเข้าใจของฉันว่าข้อยกเว้นไม่ใช่ข้อผิดพลาดพวกเขาควรจะใช้สำหรับเงื่อนไขพิเศษเท่านั้น"

ในบางภาษาอื่น ๆ กฎนั้นสะท้อนถึงบรรทัดฐานทางวัฒนธรรมที่สะท้อนอยู่ในห้องสมุดของพวกเขา "กฎ" ยังขึ้นอยู่กับส่วนหนึ่งของการพิจารณาประสิทธิภาพสำหรับภาษาเหล่านั้น

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

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

"อย่างไรก็ตามถ้ามันรวมอยู่ในภาษาของตัวเองมันจะต้องมีเหตุผลที่ดีสำหรับมันใช่ไหม?"

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

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

มีความเป็นโพสต์บล็อกที่ดีใน indispensibility ข้อยกเว้นที่นี่

ดูคำตอบ Stack Overflow นี้ด้วย: มีข้อยกเว้นจริงๆสำหรับข้อผิดพลาดพิเศษหรือไม่

"อะไรเป็นเหตุผลที่ทำให้การลองยกเว้นอื่นมีอยู่"

ข้ออื่นนั้นน่าสนใจ มันจะทำงานเมื่อไม่มีข้อยกเว้น แต่ก่อนหน้าข้อสุดท้าย นั่นคือจุดประสงค์หลักของมัน

ตัวเลือกเดียวในการเรียกใช้รหัสเพิ่มเติมก่อนที่จะสรุปก็คือวิธีปฏิบัติที่เงอะงะของการเพิ่มรหัสลงใน try-clause นั่นคือเงอะงะเพราะมันมีความเสี่ยงในการเพิ่มข้อยกเว้นในรหัสที่ไม่ได้ตั้งใจที่จะได้รับการคุ้มครองโดยลองบล็อก

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

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

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

อีกตัวอย่างหนึ่งที่เกิดขึ้นในนักวิ่งที่ไม่เกี่ยวข้อง:

try:
    tests_run += 1
    run_testcase(case)
except Exception:
    tests_failed += 1
    logging.exception('Failing test case: %r', case)
    print('F', end='')
else:
    logging.info('Successful test case: %r', case)
    print('.', end='')

สุดท้ายการใช้ประโยคอื่น ๆ ที่พบบ่อยที่สุดใน try-block นั้นมีไว้เพื่อความสวยงามเล็กน้อย การใช้งานนี้เป็นตัวเลือกเสมอและไม่จำเป็นต้องเคร่งครัด


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

2
ขอบคุณสำหรับคำตอบ. สำหรับผู้อ่านที่กำลังมองหาตัวอย่างของการใช้งานลองยกเว้นอื่นดูที่วิธี copyfile ของ Shutil github.com/python/cpython/blob/master/Lib/shutil.py#L244
suripoori

2
ประเด็นก็คือประโยคอื่นจะถูกดำเนินการเฉพาะเมื่อลองมาตราประสบความสำเร็จ
Jonathan

172

อะไรคือเหตุผลที่ทำให้การลองยกเว้นอื่นมีอยู่

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

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

ตัวอย่าง

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

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

"การพยายามยกเว้น" ห้องสวีทมีสองคำสั่งตัวเลือกและelse ดังนั้นจึงเป็นจริงfinallytry-except-else-finally

elseจะประเมินก็ต่อเมื่อไม่มีข้อยกเว้นจากtryบล็อก มันช่วยให้เราลดความซับซ้อนของรหัสที่ซับซ้อนด้านล่าง:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

ดังนั้นหากเราเปรียบเทียบelseกับทางเลือกอื่น (ซึ่งอาจสร้างข้อบกพร่อง) เราจะเห็นว่ามันลดบรรทัดของรหัสและเราจะสามารถอ่านได้ง่ายขึ้นบำรุงรักษาและมีรหัสฐานรถบั๊กน้อย

finally

finally จะดำเนินการไม่ว่าจะเกิดอะไรขึ้นแม้ว่าบรรทัดอื่นจะถูกประเมินด้วยคำสั่ง return

แยกย่อยด้วยรหัสหลอก

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

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

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

มันเป็นความจริงที่เราสามารถรวมรหัสในelseบล็อกในtryบล็อกแทนซึ่งมันจะทำงานหากไม่มีข้อยกเว้น แต่ถ้ารหัสตัวเองนั้นยกข้อยกเว้นประเภทที่เรากำลังจับอยู่ การปล่อยไว้ในtryบล็อกจะเป็นการซ่อนบั๊กนั้น

เราต้องการย่อบรรทัดโค้ดในtryบล็อกให้น้อยที่สุดเพื่อหลีกเลี่ยงการจับข้อยกเว้นที่เราไม่คาดคิดภายใต้หลักการที่ว่าหากโค้ดของเราล้มเหลวเราต้องการให้มันล้มเหลวอย่างดัง นี่คือวิธีที่ดีที่สุด

ฉันเข้าใจว่าข้อยกเว้นไม่ใช่ข้อผิดพลาด

ใน Python ข้อยกเว้นส่วนใหญ่เป็นข้อผิดพลาด

เราสามารถดูลำดับชั้นข้อยกเว้นโดยใช้ pydoc ตัวอย่างเช่นใน Python 2:

$ python -m pydoc exceptions

หรือ Python 3:

$ python -m pydoc builtins

จะให้ลำดับชั้นกับเรา เราจะเห็นว่าส่วนใหญ่Exceptionเป็นข้อผิดพลาดแม้ว่า Python จะใช้บางอย่างสำหรับสิ่งต่าง ๆ เช่นสิ้นสุดforลูป ( StopIteration) นี่คือลำดับชั้นของ Python 3:

BaseException
    Exception
        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
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

ผู้วิจารณ์ถามว่า:

สมมติว่าคุณมีวิธีที่ส่ง Ping ไปยัง API ภายนอกและคุณต้องการจัดการข้อยกเว้นที่คลาสภายนอก API wrapper คุณเพียงแค่คืนค่า e จากเมธอดภายใต้ข้อยกเว้นที่ e เป็นวัตถุข้อยกเว้น?

ไม่คุณจะไม่ส่งคืนข้อยกเว้นเพียงแค่ทำการเปิดมันด้วยเปลือยraiseเพื่อรักษาสแต็คเทรซ

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

หรือใน Python 3 คุณสามารถเพิ่มข้อยกเว้นใหม่และรักษา backtrace ด้วยการโยงข้อยกเว้น:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

ผมทำอย่างละเอียดในคำตอบของฉันที่นี่


upvoted! คุณมักจะทำอะไรในส่วนที่จับ? สมมติว่าคุณมีเมธอดที่ ping API ภายนอกและคุณต้องการจัดการข้อยกเว้นที่คลาสภายนอก wrapper API คุณเพียงแค่คืนค่า e จากเมธอดภายใต้ข้อยกเว้นที่ e เป็นวัตถุข้อยกเว้น?
PirateApp

1
@PirateApp ขอบคุณ! ไม่ไม่ส่งคืนคุณควรจะraiseอ่านซ้ำหรือทำข้อยกเว้น - แต่มีหัวข้อเพิ่มเติมและครอบคลุมที่นี่: stackoverflow.com/q/2052390/541136 - ฉันอาจลบความคิดเห็นเหล่านี้หลังจากที่ฉันได้ เห็นคุณได้เห็นพวกเขา
Aaron Hall

ขอบคุณมากสำหรับรายละเอียด! จะผ่านการโพสต์ตอนนี้
PirateApp

36

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

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

ลองนึกภาพคุณมีสถานการณ์ที่คุณป้อนข้อมูลผู้ใช้บางอย่างที่ต้องดำเนินการ แต่มีค่าเริ่มต้นที่ประมวลผลแล้ว try: ... except: ... else: ...โครงสร้างทำให้การอ่านรหัสมาก:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

เปรียบเทียบกับวิธีใช้งานในภาษาอื่น:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

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

ตัวอย่างมีการประดิษฐ์เล็กน้อยตามธรรมชาติ แต่มันแสดงให้เห็นว่ามีบางกรณีสำหรับโครงสร้างนี้


15

เป็นวิธีปฏิบัติที่ดีในการใช้ try-exception-else ใน python หรือไม่?

คำตอบของเรื่องนี้คือมันขึ้นอยู่กับบริบท หากคุณทำสิ่งนี้:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

มันแสดงให้เห็นว่าคุณไม่รู้จัก Python เป็นอย่างดี ฟังก์ชั่นนี้ถูกห่อหุ้มในdict.getวิธีการ:

item = d.get('item', 'default')

try/ exceptบล็อกเป็นรกมากขึ้นสายตาและละเอียดวิธีการเขียนสิ่งที่สามารถดำเนินการได้อย่างมีประสิทธิภาพในบรรทัดเดียวด้วยวิธีอะตอม มีอีกหลายกรณีที่เป็นจริง

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

อ่านZen of Pythonทำความเข้าใจว่ามีหลักการที่อยู่ในความตึงเครียดและระมัดระวังความเชื่อที่ต้องพึ่งพาคำพูดใด ๆ


12

ดูตัวอย่างต่อไปนี้ซึ่งแสดงให้เห็นถึงทุกอย่างเกี่ยวกับลอง - ยกเว้น - อื่น - ในที่สุด:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

ติดตั้งและใช้งานโดย:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

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

6

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

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

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

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

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

@Juan Antonio Gomez Moriano บล็อคการเข้ารหัสของฉันมีวัตถุประสงค์เพื่อเป็นตัวอย่างเท่านั้น ฉันอาจจะไม่ใช้ pass อย่างใดอย่างหนึ่ง
Greg

4

เมื่อใดก็ตามที่คุณเห็นสิ่งนี้:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

หรือแม้แต่สิ่งนี้:

try:
    return 1 / x
except ZeroDivisionError:
    return None

พิจารณาสิ่งนี้แทน:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x

1
ไม่ตอบคำถามของฉันเพราะมันเป็นเพียงตัวอย่างของเพื่อนของฉัน
Juan Antonio Gomez Moriano

ใน Python ข้อยกเว้นไม่ใช่ข้อผิดพลาด พวกเขาไม่ได้ยอดเยี่ยม ใน Python เป็นเรื่องปกติและเป็นธรรมชาติที่จะใช้ข้อยกเว้นสำหรับการควบคุมการไหล สิ่งนี้พิสูจน์ได้จากการรวม contextlib.suppress () ในไลบรารีมาตรฐาน ดูคำตอบของ Raymond Hettinger ได้ที่นี่: stackoverflow.com/a/16138864/1197429 (Raymond เป็นผู้สนับสนุนหลักของ Python และมีอำนาจในทุกสิ่ง Pythonic!)
Rajiv Bakulesh Shah

4

เพียงเพราะไม่มีใครโพสต์ความคิดเห็นนี้ฉันจะบอกว่า

หลีกเลี่ยงelseข้อtry/excepts เนื่องจากพวกเขาไม่คุ้นเคยกับคนส่วนใหญ่

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

(ฉันเขียนคำตอบนี้อย่างแม่นยำเพราะฉันพบtry/except/elseใน codebase ของฉันและมันทำให้ช่วงเวลา wtf และบังคับให้ฉันทำ googling)

ดังนั้นทุกที่ที่ฉันเห็นรหัสเช่นตัวอย่าง OP:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

ฉันต้องการ refactor ถึง

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    return  # <1>
# do some more processing in non-exception case  <2>
return something
  • <1> ผลตอบแทนที่ชัดเจนแสดงให้เห็นอย่างชัดเจนว่าในกรณียกเว้นเราทำงานเสร็จแล้ว

  • <2> ในฐานะที่เป็นผลข้างเคียงเล็กน้อยที่ดีรหัสที่เคยอยู่ในelseบล็อกนั้นได้รับการอุทิศโดยระดับหนึ่ง


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

2

นี่เป็นตัวอย่างง่ายๆของฉันในการทำความเข้าใจกับบล็อก try-exception-else-final ใน Python:

def div(a, b):
    try:
        a/b
    except ZeroDivisionError:
        print("Zero Division Error detected")
    else:
        print("No Zero Division Error")
    finally:
        print("Finally the division of %d/%d is done" % (a, b))

ลอง div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

ลอง div 1/0 กัน

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

1
ฉันคิดว่านี่เป็นตัวอย่างที่ไม่สามารถอธิบายได้ว่าทำไมคุณถึงไม่สามารถใส่รหัสอื่นในการทดลองได้
Mojimi

-4

OP คุณถูกต้อง อื่นหลังจากลอง / ยกเว้นในหลามน่าเกลียด มันนำไปสู่วัตถุควบคุมการไหลอื่นที่ไม่ต้องการ:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

การเทียบเท่าที่ชัดเจนโดยสิ้นเชิงคือ:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

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

เพียงเพราะคุณสามารถทำสิ่งใดไม่ได้หมายความว่าคุณควรทำอะไร

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

แค่ 5 เซ็นต์ของฉันที่นี่ ฉันต้องตามหลังมาและทำความสะอาดโค้ดจำนวนมากที่เขียนโดยนักพัฒนาวิทยาลัยชั้นปีที่ 1 ซึ่งคิดว่าพวกเขาฉลาดและต้องการเขียนโค้ดด้วยวิธีที่แน่นหนาและมีประสิทธิภาพ uber เมื่อมันทำให้ยุ่งเหยิง เพื่อลองอ่าน / แก้ไขในภายหลัง ฉันโหวตให้อ่านได้ทุกวันและสองครั้งในวันอาทิตย์


15
คุณถูก. นั่นคือทั้งหมดที่ชัดเจนและเทียบเท่า ... เว้นแต่ว่ามันเป็นprintคำสั่งของคุณที่ล้มเหลว จะเกิดอะไรขึ้นถ้าx = blah()ส่งคืน a strแต่ข้อความสั่งพิมพ์ของคุณคือprint 'just succeeded with blah. x == %d' % xอะไร ตอนนี้คุณมีสิ่งมีTypeErrorชีวิตที่คุณไม่ได้เตรียมรับมือ คุณกำลังตรวจสอบx = blah()เพื่อค้นหาแหล่งที่มาของข้อยกเว้นและยังไม่มีแม้แต่ที่นั่น ฉันได้ทำสิ่งนี้ (หรือเทียบเท่า) มากกว่าหนึ่งครั้งซึ่งสิ่งที่elseจะทำให้ฉันทำผิดพลาดนี้ ตอนนี้ฉันรู้ดีขึ้นแล้ว :-D
Doug R.

2
... และใช่คุณพูดถูก elseประโยคไม่ได้เป็นคำสั่งที่สวยและจนกว่าคุณจะใช้มันก็ไม่ได้ง่าย แต่finallyเมื่อก่อนฉันก็เริ่มใช้มันไม่ได้ ...
Doug R.

2
สะท้อนดั๊กอาร์ก็ไม่เทียบเท่าเพราะข้อยกเว้นในช่วงงบในส่วนelseข้อจะไม่exceptจับโดย
alastair

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

1
@DougR "คุณกำลังตรวจx = blah()หาแหล่งที่มาของข้อยกเว้น" tracebackทำไมคุณต้องตรวจสอบแหล่งที่มาของข้อยกเว้นจากที่ที่ไม่ถูกต้อง
nehem
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.