เกี่ยวกับการจับข้อยกเว้นใด ๆ


698

ฉันจะเขียนtry/ exceptบล็อกที่จับข้อยกเว้นทั้งหมดได้อย่างไร


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

12
เพื่อให้แม่นยำยิ่งขึ้นการจับข้อยกเว้นที่เป็นไปได้ทั้งหมดนั้นเป็นเพียงปัญหาหากถูกจับอย่างเงียบ ๆ เป็นการยากที่จะคิดว่าวิธีนี้เหมาะสมที่อื่นนอกเหนือจากที่ข้อความแสดงข้อผิดพลาดที่จับได้ถูกพิมพ์sys.stderrและอาจถูกบันทึกไว้ นั่นเป็นข้อยกเว้นที่สมบูรณ์และเป็นเรื่องปกติ
Evgeni Sergeev

คุณไม่ลอง: try: whatever() except Exception as e: exp_capture() ?
Charlie Parker

คำตอบ:


564

คุณสามารถ แต่คุณอาจไม่ควร:

try:
    do_something()
except:
    print "Caught it!"

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

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as (errno, strerror):
    print "I/O error({0}): {1}".format(errno, strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

30
วิธีแก้ปัญหาที่เป็นไปได้: effbot.org/zone/stupid-exceptions-keyboardinterrupt.htm
Mikel

15
คำสั่งสุดท้ายของคุณไม่เป็นความจริงคุณจะต้องพูดอย่างชัดเจนexcept Exception:ยกเว้นว่าคุณมีที่นั่นก็จะจับ BaseException คน
Pykler

7
คุณควรพิมพ์ไปที่ stderr
nyuszika7h

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

26
สิ่งที่ฉันคิดว่าน่าแปลกก็คือในภาษาที่พิมพ์เป็ดซึ่งคุณไม่ได้ประกาศตัวแปรอินสแตนซ์มันก็เป็นห่วงอย่างมากที่จะไม่พิมพ์ข้อยกเว้นทั้งหมดของคุณ อืม!
Blaze

839

นอกเหนือจากexcept:ข้อเปล่า(ซึ่งคนอื่นบอกว่าคุณไม่ควรใช้) คุณสามารถจับException:

import traceback
import logging

try:
    whatever()
except Exception as e:
    logging.error(traceback.format_exc())
    # Logs the error appropriately. 

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

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


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

6
สำหรับทุกคนที่สงสัยว่าขัดกับความคาดหวังของฉันทั้งหมดนี้จะยังคงจับสิ่งที่ไม่ใช่คลาสย่อยเช่น ints อย่างน้อยใน python 2.x
โจเซฟการ์วิน

5
@JosephGarvin ที่ไม่ถูกต้องเช่นนี้จะไม่จับ "ไม่ใช่ข้อยกเว้น" ที่ทำไม่ได้ Exceptionsubclass โปรดทราบว่ามันเป็นไปไม่ได้ที่จะเพิ่มintข้อยกเว้นและพยายามที่จะยกTypeErrorข้อยกเว้นซึ่งเป็นสิ่งที่จะถูกจับโดยexcept Exceptionประโยคปิดล้อมในกรณีดังกล่าว ในทางกลับกันคลาสแบบเก่าสามารถยกขึ้นและมีคุณสมบัติเป็น "ไม่ใช่ข้อยกเว้น" ที่ไม่ได้คลาสย่อยException- นี้จะถูกจับโดยexceptประโยคเปล่าแต่ไม่ใช่โดยexcept Exceptionประโยค
Yoel

4
@JosephGarvin ตรวจสอบรายการบล็อกนี้: chris-lamb.co.uk/posts/no-one-expects-string-literal-exception ฉันอยู่กับ @Yoel ในรายการนี้การทดสอบของคุณเพิ่งจะปิดบังTypeError
Duncan

2
@ CharlieParker ไม่มีอะไรผิดปกติกับการจับพวกเขาถ้านั่นคือสิ่งที่คุณต้องการ แต่คุณส่วนใหญ่ไม่ได้ การโทรsys.exit()มักจะหมายความว่าคุณคาดหวังว่าแอปจะยุติ แต่ถ้าคุณจับ SystemExit มันจะไม่ เช่นเดียวกันถ้าคุณกด control-C บนสคริปต์ที่กำลังรัน (Ctrl-break บน windows) คุณคาดว่าโปรแกรมจะหยุดไม่จับข้อผิดพลาดและดำเนินต่อไป แต่คุณสามารถจับทั้งสองอย่างนี้ถ้าคุณต้องการล้างก่อนที่จะมีอยู่
Duncan

100

คุณสามารถทำได้เพื่อจัดการข้อยกเว้นทั่วไป

try:
    a = 2/0
except Exception as e:
    print e.__doc__
    print e.message

8
สิ่งนี้อาจไม่ได้รับการยกเว้นทั้งหมดเนื่องจากคลาสพื้นฐานสำหรับข้อยกเว้นทั้งหมดคือ BaseException และฉันพบรหัสการผลิตที่ไม่ได้อยู่ในตระกูลคลาสข้อยกเว้น ดูdocs.python.org/3/library/…สำหรับรายละเอียดเกี่ยวกับสิ่งนี้
DDay

4
สิ่งนี้ไม่ได้รับการยกเว้นทั้งหมด
Andy_A̷n̷d̷y̷

6
ในทางเทคนิคมันควรตรวจจับข้อยกเว้นทั้งหมดที่ไม่ได้ออกจากระบบ จาก docs @Day ที่ลิงก์: " exception BaseException: คลาสพื้นฐานสำหรับข้อยกเว้นในตัวทั้งหมดไม่ได้หมายความว่าจะสืบทอดโดยตรงจากคลาสที่ผู้ใช้กำหนดเอง (สำหรับกรณีนั้นให้ใช้ Exception)" นอกจากว่าคุณกำลังทำงานกับรหัสที่ไม่สนใจสิ่งนี้หรือคุณจำเป็นต้องตรวจสอบข้อยกเว้นที่ออกจากระบบสิ่งที่กล่าวมาข้างต้นควรจะใช้ได้
Peter Cassetta

@PeterCassetta เมื่อใดที่คุณต้องการจับระบบออกจากข้อยกเว้น? ดูเหมือนว่ากระทู้ทั่วไปในคำถามที่เราไม่ต้องการจับสิ่งเหล่านี้ แต่ฉันไม่เข้าใจว่าทำไม ทำไมไม่ปกติ
Charlie Parker

68

BaseExceptionการจับข้อยกเว้นเป็นไปได้ทั้งหมดจับ มันอยู่ด้านบนของลำดับชั้นข้อยกเว้น:

Python 3: https://docs.python.org/3.5/library/exceptions.html#exception-hierarchy

Python 2.7: https://docs.python.org/2.7/library/exceptions.html#exception-hierarchy

try:
    something()
except BaseException as error:
    print('An exception occurred: {}'.format(error))

แต่ตามที่คนอื่นพูดถึงคุณมักจะไม่ต้องการสิ่งนี้เฉพาะกรณีที่เฉพาะเจาะจง


1
ต้องการบันทึกความคืบหน้าของงานที่ใช้เวลานานหลังจากกด Ctrl-C ผิดปกติหรือไม่?
BallpointBen

54

ตัวอย่างที่ง่ายมากคล้ายกับตัวอย่างที่พบที่นี่:

http://docs.python.org/tutorial/errors.html#defining-clean-up-actions

หากคุณกำลังพยายามที่จะรับข้อยกเว้นทั้งหมดให้วางโค้ดทั้งหมดของคุณไว้ในคำสั่ง "ลอง:" แทนที่ 'พิมพ์ "การดำเนินการที่อาจทำให้เกิดข้อยกเว้น"'

try:
    print "Performing an action which may throw an exception."
except Exception, error:
    print "An exception was thrown!"
    print str(error)
else:
    print "Everything looks great!"
finally:
    print "Finally is called directly after executing the try statement whether an exception is thrown or not."

ในตัวอย่างด้านบนคุณจะเห็นผลลัพธ์ตามลำดับนี้:

1) ทำการกระทำที่อาจทำให้เกิดข้อยกเว้น

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

3) "มีข้อผิดพลาดเกิดขึ้น!" หรือ "ทุกอย่างดูดี!" ขึ้นอยู่กับว่าข้อยกเว้นถูกโยน

หวังว่านี่จะช่วยได้!


26

มีหลายวิธีในการทำเช่นนี้โดยเฉพาะกับ Python 3.0 ขึ้นไป

วิธีที่ 1

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

def bad_method():
    try:
        sqrt = 0**-1
    except Exception as e:
        print(e)

bad_method()

วิธีที่ 2

แนะนำวิธีการนี้เนื่องจากให้รายละเอียดเพิ่มเติมเกี่ยวกับข้อยกเว้นแต่ละข้อ มันรวมถึง:

  • หมายเลขบรรทัดสำหรับรหัสของคุณ
  • ชื่อไฟล์
  • ข้อผิดพลาดที่เกิดขึ้นจริงในทาง verbose มากขึ้น

ข้อเสียเปรียบเพียงอย่างเดียวคือ tracback จำเป็นต้องนำเข้า

import traceback

def bad_method():
    try:
        sqrt = 0**-1
    except Exception:
        print(traceback.print_exc())

bad_method()

21

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

try:
    raise IndexError #as test error
except Exception as e:
    excepName = type(e).__name__ # returns the name of the exception

2
try:
    whatever()
except:
    # this will catch any exception or error

เป็นมูลค่าการกล่าวขวัญนี้ไม่เหมาะสม Python coding สิ่งนี้จะตรวจจับข้อผิดพลาดมากมายที่คุณอาจไม่ต้องการตรวจจับ


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