หลาม: ฉันจะรู้ได้อย่างไรว่าเกิดข้อยกเว้นประเภทใดขึ้น


230

ฉันมีฟังก์ชั่นที่เรียกโดยโปรแกรมหลัก:

try:
    someFunction()
except:
    print "exception happened!"

แต่ในช่วงกลางของการดำเนินการของฟังก์ชั่นมันทำให้เกิดข้อยกเว้นจึงข้ามไปยังexceptส่วน

ฉันจะดูสิ่งที่เกิดขึ้นในสิ่งsomeFunction()ที่ทำให้เกิดข้อยกเว้นเกิดขึ้นได้อย่างไร


9
ไม่เคยใช้งาน bare except:(โดยไม่ใช้ bare raise) ยกเว้นอาจจะใช้เพียงครั้งเดียวต่อโปรแกรมและไม่ควรใช้ตอนนั้น
Mike Graham

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

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

3
Inside exceptblock มีข้อยกเว้นผ่านsys.exc_info()ฟังก์ชั่น - ฟังก์ชั่นนี้จะคืนค่า tuple ของสามค่าที่ให้ข้อมูลเกี่ยวกับข้อยกเว้นที่กำลังถูกจัดการอยู่ในปัจจุบัน
Piotr Dobrogost

คำตอบ:


384

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

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

  • แสดงข้อยกเว้นเป็นกล่องโต้ตอบใน GUI
  • ถ่ายโอนข้อยกเว้นจากเธรดผู้ปฏิบัติงานหรือกระบวนการไปยังเธรดการควบคุมหรือกระบวนการในแอ็พพลิเคชันแบบมัลติเธรดหรือมัลติโพรเซสเซอร์

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

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

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

ความแตกต่างระหว่างข้างบนและการใช้except:โดยไม่มีข้อโต้แย้งใด ๆ คือสองเท่า:

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

หากคุณต้องการ stacktrace เดียวกันกับที่คุณได้รับหากคุณไม่ได้รับการยกเว้นคุณสามารถได้รับสิ่งนี้ (ยังอยู่ในข้อยกเว้น):

import traceback
print traceback.format_exc()

หากคุณใช้loggingโมดูลคุณสามารถพิมพ์ข้อยกเว้นลงในบันทึก (พร้อมข้อความ) ดังนี้:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

หากคุณต้องการขุดลึกลงไปและตรวจสอบสแต็กดูตัวแปร ฯลฯ ใช้post_mortemฟังก์ชันของpdbโมดูลภายในบล็อกยกเว้น:

import pdb
pdb.post_mortem()

ฉันพบวิธีสุดท้ายนี้จะมีค่าเมื่อล่าสัตว์ลงบั๊ก


1
traceback.print_exc () จะทำสิ่งเดียวกับที่ซับซ้อนยิ่งขึ้นของคุณ ""
Gurgeh

1
@Gurgeh ใช่ แต่ฉันไม่ทราบว่าเขาต้องการพิมพ์หรือบันทึกลงในไฟล์หรือบันทึกหรือทำอย่างอื่นด้วย
Lauritz V. Thaulow

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

10
@Rik ฉันคิดว่าคุณอาจต้องการทั้งหมดนี้ ตัวอย่างเช่นหากคุณมีโปรแกรมที่มี GUI และแบ็กเอนด์และคุณต้องการแสดงข้อยกเว้นทั้งหมดจากแบ็กเอนด์เป็นข้อความ GUI แทนที่จะให้โปรแกรมของคุณเลิกด้วยการติดตามสแต็ก ในกรณีเช่นนี้คุณควรตรวจจับข้อยกเว้นทั่วไปสร้างข้อความการสืบค้นกลับสำหรับกล่องโต้ตอบบันทึกข้อยกเว้นด้วยและถ้าอยู่ในโหมดดีบักให้ป้อน post-mortem
Lauritz V. Thaulow

18
@RikPoggi: ความคิดที่ไร้เดียงสา มีสถานการณ์ที่เหมาะสมหลายประการเมื่อคุณต้องการตรวจจับข้อยกเว้นจากรหัสของคนอื่นและคุณไม่รู้ว่าจะมีข้อยกเว้นใดเกิดขึ้น
stackoverflowuser2010

63

รับชื่อของคลาสที่วัตถุยกเว้นเป็นของ:

e.__class__.__name__

และการใช้ฟังก์ชัน print_exc () จะพิมพ์การติดตามสแต็กซึ่งเป็นข้อมูลที่จำเป็นสำหรับข้อความแสดงข้อผิดพลาด

แบบนี้:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

คุณจะได้ผลลัพธ์ดังนี้:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

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

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

เอาท์พุท:

special case of CustomException not interfering

และล่ามพิมพ์ข้อยกเว้น:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

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

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

เอาท์พุท:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

แจ้งให้ทราบว่า traceback ไม่รวมถึงcalculate()ฟังก์ชั่นจากบรรทัดซึ่งเป็นที่มาของข้อยกเว้นเดิม9e


หากคุณต้องการเก็บข้อมูลการสืบค้นกลับเป็นสตริงคุณสามารถใช้งานได้traceback.format_exc()เช่นกัน
Stevoisiak

1
e.__class__.__name__นี้เป็นเช่นเดียวกับtype(e).__name__ที่แนะนำโดยคำตอบข้างต้น?
information_interchange

1
@information_interchange ใช่ คำถามและเนื้อหาคำตอบที่ยอมรับเปลี่ยนไปตามกาลเวลา เป็นเรื่องน่าละอายที่ผู้เข้าร่วมรายอื่นจะไม่ได้รับแจ้งจาก SO SO :(
Alex

14

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

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
การใช้คำว่า "ไม่เคย" ที่นี่ไม่เคยผิดเลย ฉันใช้try: ... except Exception:สิ่งต่างๆมากมายเช่นการใช้ห้องสมุดที่ขึ้นอยู่กับเครือข่ายหรือหมอนวดข้อมูลที่อาจส่งของแปลก ๆ ไปให้เธอ โดยปกติฉันมีการบันทึกที่เหมาะสมเช่นกัน นี่เป็นสิ่งสำคัญที่จะช่วยให้โปรแกรมทำงานต่อไปในกรณีที่เกิดข้อผิดพลาดในข้อมูลอินพุต
thnee

3
เคยพยายามที่จะตรวจจับข้อยกเว้นทั้งหมดที่สามารถเพิ่มได้เมื่อส่งอีเมลโดยใช้smtplib?
linusg

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

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

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

10

เว้นแต่somefunctionว่าเป็นฟังก์ชั่นมรดกที่เข้ารหัสไม่ดีมากคุณไม่ควรต้องการสิ่งที่คุณขอ

ใช้หลายexceptประโยคเพื่อจัดการกับวิธีต่าง ๆ ยกเว้นต่างกัน:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

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


8
หากคุณกำลังใช้ห้องสมุดบุคคลที่สามคุณอาจไม่ทราบว่าจะมีข้อยกเว้นใดบ้างที่อยู่ภายใน คุณจะจับพวกมันทีละคนได้อย่างไร?
stackoverflowuser2010

8

คำตอบส่วนใหญ่ชี้ไปที่except (…) as (…):ไวยากรณ์ (ถูกต้อง) แต่ในขณะเดียวกันก็ไม่มีใครต้องการพูดคุยเกี่ยวกับช้างในห้องที่ช้างsys.exc_info()ทำงาน จากเอกสารของโมดูลsys (การเน้นของฉัน):

ฟังก์ชันนี้คืนค่า tuple ของสามค่าที่ให้ข้อมูลเกี่ยวกับข้อยกเว้นที่กำลังถูกจัดการอยู่ในปัจจุบัน
(…)
หากไม่มีการจัดการข้อยกเว้นที่ใดก็ได้บนสแต็กจะส่งคืน tuple ที่มีค่า None สามค่า มิฉะนั้นค่าที่ส่งคืนคือ (type, value, traceback) ความหมายของพวกเขาคือ: type รับชนิดของข้อยกเว้นที่ถูกจัดการ (คลาสย่อยของ BaseException); ค่ารับอินสแตนซ์ข้อยกเว้น (อินสแตนซ์ของประเภทข้อยกเว้น); traceback ได้รับออบเจ็กต์ traceback (ดูคู่มืออ้างอิง) ซึ่งสรุปแค็ปการโทร ณ จุดที่เกิดข้อยกเว้นเริ่มแรก

ฉันคิดว่าsys.exc_info()คำตอบนั้นตรงที่สุดกับคำถามดั้งเดิมของฉันจะรู้ได้อย่างไรว่ามีข้อยกเว้นประเภทใดเกิดขึ้น


1
exceptนั่นคือคำตอบที่ถูกต้องให้ฉันเป็นมันไม่แก้ปัญหาของสิ่งที่ยกเว้นจะเกิดขึ้นดังนั้นสิ่งที่ฉันควรจะวางแทนเปลือย เพียงเพื่อประโยชน์ของความสมบูรณ์ที่จะบอกคุณชนิดยกเว้นที่สามารถนำมาใช้ในexctype, value = sys.exc_info()[:2] except
Ondrej Burkert

5

ลอง: someFunction () ยกเว้นข้อยกเว้น exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

-1 ตามที่exc.__class__.__name__แนะนำไว้ในคำตอบของ Alex แล้ว - stackoverflow.com/a/9824060/95735
Piotr Dobrogost

3

คำตอบเหล่านี้ใช้ได้สำหรับการดีบัก แต่สำหรับการทดสอบโดยทางโปรแกรม isinstance(e, SomeException)สามารถทำได้สะดวกเนื่องจากมันจะทำการทดสอบสำหรับคลาสย่อยSomeExceptionด้วยดังนั้นคุณสามารถสร้างฟังก์ชันการทำงานที่ใช้กับลำดับชั้นของข้อยกเว้น


1

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

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

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

ในตัวอย่างที่แสดงวิธีแก้ไขปัญหาหนึ่งอาจค้นหาข้อมูลแอปที่จัดเก็บไว้ที่อื่นกล่าวว่าไฟล์ 'app.p' ถูกลบโดยไม่ได้ตั้งใจ

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


0

เพื่อเพิ่มคำตอบของ Lauritz ฉันได้สร้างมัณฑนากร / wrapper สำหรับการจัดการข้อยกเว้นและบันทึกของ wrapper ว่าเกิดข้อยกเว้นประเภทใด

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

สิ่งนี้สามารถเรียกได้ในวิธีการเรียนหรือฟังก์ชั่นแบบสแตนด์อโลนกับมัณฑนากร:

@general_function_handler

ดูบล็อกของฉันเกี่ยวกับตัวอย่างเต็ม: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

คุณสามารถเริ่มได้ตามที่ Lauritz แนะนำด้วย:

except Exception as ex:

แล้วก็print exชอบ:

try:
    #your try code here
except Exception as ex:
    print ex

คุณช่วยอธิบายหน่อยได้ไหมว่าคำตอบของคุณโดดเดี่ยว?
GHC

1
แน่ใจว่าคุณสามารถพิมพ์ข้อยกเว้นจับเช่นนี้ลอง: รหัสลอง #your นี่ยกเว้นข้อยกเว้นเป็นอดีต: อดีตพิมพ์ตอนนี้ข้อผิดพลาดจะถูกพิมพ์
Gura

-2

ข้อยกเว้นที่เกิดขึ้นจริงสามารถบันทึกในวิธีต่อไปนี้:

try:
    i = 1/0
except Exception as e:
    print e

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับการยกเว้นจากงูใหญ่กวดวิชา


-2

คำถามของคุณคือ: "ฉันจะดูได้อย่างไรว่าเกิดอะไรขึ้นใน someFunction () ที่ทำให้เกิดข้อยกเว้นเกิดขึ้น"

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

วิธีที่ง่ายที่สุดคือการใช้ดีบักเกอร์ที่สามารถหยุดในกรณีที่เกิดข้อยกเว้นที่ไม่ถูกตรวจจับโดยเฉพาะอย่างยิ่งไม่ต้องออกเพื่อให้คุณสามารถตรวจสอบตัวแปรได้ ตัวอย่างเช่น PyDev ใน IDE โอเพนซอร์ส Eclipse สามารถทำได้ ต้องการเปิดใช้งานว่าใน Eclipse เปิดมุมมอง Debug เลือกManage Python Exception BreakpointsในเมนูและการตรวจสอบRunSuspend on uncaught exceptions


-4

เพียงงดเว้นจากข้อยกเว้นและการย้อนกลับที่ Python พิมพ์จะบอกคุณว่าเกิดข้อยกเว้นอะไรขึ้น

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