ฉันจะเพิ่มข้อยกเว้นใน Python เพื่อให้สามารถจับได้ในภายหลังผ่านทางexcept
บล็อกได้อย่างไร
ฉันจะเพิ่มข้อยกเว้นใน Python เพื่อให้สามารถจับได้ในภายหลังผ่านทางexcept
บล็อกได้อย่างไร
คำตอบ:
ฉันจะโยน / ยกระดับข้อยกเว้นใน Python ด้วยตนเองได้อย่างไร
ใช้เฉพาะคอนสตรัคข้อยกเว้นความหมายมากที่สุดที่เหมาะกับปัญหาของคุณได้
เจาะจงข้อความของคุณเช่น:
raise ValueError('A very specific bad thing happened.')
Exception
หลีกเลี่ยงการเลี้ยงทั่วไป ในการจับมันคุณจะต้องตรวจจับข้อยกเว้นเฉพาะอื่น ๆ ทั้งหมดที่ซับคลาสนั้น
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
ตัวอย่างเช่น:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
และการจับที่เฉพาะเจาะจงมากขึ้นจะไม่เป็นข้อยกเว้นทั่วไป:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
raise
ข้อความแต่ให้ใช้ตัวสร้างข้อยกเว้นเฉพาะเจาะจงมากที่สุดที่ความหมายเหมาะกับปัญหาของคุณได้
raise ValueError('A very specific bad thing happened')
ซึ่งคล่องแคล่วอนุญาตให้จำนวนอาร์กิวเมนต์โดยพลการจะถูกส่งผ่านไปยังตัวสร้าง:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
อาร์กิวเมนต์เหล่านี้เข้าถึงได้โดยargs
แอตทริบิวต์บนException
วัตถุ ตัวอย่างเช่น:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
พิมพ์
('message', 'foo', 'bar', 'baz')
ใน Python 2.5 จะมีการmessage
เพิ่มแอตทริบิวต์จริงเพื่อBaseException
สนับสนุนผู้ใช้ให้ยกเว้นคลาสย่อยและหยุดใช้args
แต่การแนะนำmessage
และการคัดค้าน args ดั้งเดิมได้ถูกถอนออก
except
ข้อเมื่ออยู่ในข้อยกเว้นคุณอาจต้องการบันทึกว่ามีข้อผิดพลาดประเภทใดประเภทหนึ่งเกิดขึ้นแล้วจึงยกขึ้นอีกครั้ง วิธีที่ดีที่สุดในการทำเช่นนี้ในขณะที่รักษาสแต็กติดตามคือการใช้คำสั่งยกเปลือย ตัวอย่างเช่น:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
คุณสามารถรักษา stacktrace (และค่าความผิดพลาด) ด้วยsys.exc_info()
แต่นี่เป็นวิธีที่ผิดพลาดมากขึ้นและมีปัญหาความเข้ากันได้ระหว่าง Python 2 และ 3ชอบที่จะใช้เปล่าraise
เพื่อเพิ่มอีกครั้ง
เพื่ออธิบาย - การsys.exc_info()
คืนค่าประเภทค่าและการติดตามกลับ
type, value, traceback = sys.exc_info()
นี่คือไวยากรณ์ใน Python 2 - โปรดทราบว่านี่ไม่เข้ากันกับ Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
หากคุณต้องการคุณสามารถแก้ไขสิ่งที่เกิดขึ้นกับการเพิ่มใหม่ของคุณ - เช่นการตั้งค่าใหม่args
สำหรับอินสแตนซ์:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
และเราได้เก็บการติดตามย้อนกลับทั้งหมดไว้ในขณะที่แก้ไข args โปรดทราบว่านี่ไม่ใช่วิธีปฏิบัติที่ดีที่สุดและเป็นไวยากรณ์ที่ไม่ถูกต้องใน Python 3 (ทำให้การทำงานร่วมกันได้ยากขึ้นมาก)
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
ในPython 3 :
raise error.with_traceback(sys.exc_info()[2])
อีกครั้ง: หลีกเลี่ยงการจัดการการติดตามย้อนกลับด้วยตนเอง มันมีประสิทธิภาพน้อยกว่าและข้อผิดพลาดง่าย และถ้าคุณใช้เธรดและsys.exc_info
คุณอาจได้รับการติดตามย้อนกลับที่ไม่ถูกต้อง (โดยเฉพาะถ้าคุณใช้การจัดการข้อยกเว้นสำหรับโฟลว์การควบคุม - ซึ่งโดยส่วนตัวแล้วฉันมักจะหลีกเลี่ยง)
ใน Python 3 คุณสามารถโยงข้อยกเว้นซึ่งเก็บการติดตามย้อนกลับไว้ได้:
raise RuntimeError('specific message') from error
ระวัง:
สิ่งเหล่านี้สามารถซ่อนและแม้แต่ในรหัสการผลิตได้อย่างง่ายดาย คุณต้องการที่จะเพิ่มข้อยกเว้นและทำพวกเขาจะยกข้อยกเว้นแต่ไม่ใช่คนที่ตั้งใจ!
ใช้ได้ใน Python 2 แต่ไม่ใช่ใน Python 3ดังต่อไปนี้:
raise ValueError, 'message' # Don't do this, it's deprecated!
ใช้งานได้เฉพาะใน Python เวอร์ชันเก่ากว่า (2.4 หรือต่ำกว่า) คุณยังอาจเห็นคนกำลังเพิ่มสตริง:
raise 'message' # really really wrong. don't do this.
ในรุ่นที่ทันสมัยทั้งหมดนี้จะเพิ่มจริง ๆTypeError
เพราะคุณไม่ได้เพิ่มBaseException
ประเภท หากคุณไม่ได้ตรวจสอบข้อยกเว้นที่ถูกต้องและไม่มีผู้ตรวจทานที่ทราบถึงปัญหานั้นอาจเกิดจากการผลิต
ฉันเพิ่มข้อยกเว้นเพื่อเตือนผู้บริโภคเกี่ยวกับ API หากพวกเขาใช้งานไม่ถูกต้อง:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
"ฉันต้องการทำข้อผิดพลาดตามวัตถุประสงค์เพื่อที่จะเข้าสู่ข้อยกเว้น"
คุณสามารถสร้างประเภทข้อผิดพลาดของคุณเองได้หากคุณต้องการระบุบางสิ่งบางอย่างที่ไม่ถูกต้องกับแอปพลิเคชันของคุณเพียงแค่ซับคลาสที่เหมาะสมในลำดับชั้นของข้อยกเว้น:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
และการใช้งาน:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]
ดูเหมือนว่าจะทำสิ่งที่ฉันต้องการและฉันไม่เคยเจอปัญหากับมัน แต่มันก็รู้สึกแฮ็คไม่ใช่การฝึกฝน มีวิธีที่ดีกว่า?
Exception
คลาสผู้ปกครองของคุณ - คุณสามารถทำคลาสย่อยให้เฉพาะเจาะจงมากขึ้นและควรทำเช่นนั้นหากเหมาะสม
AppError
ข้อยกเว้นที่ไม่ได้กำหนด การใช้ข้อผิดพลาดในตัวอาจจะดีกว่าAttributeError
อย่าทำอย่างนี้ การเลี้ยงเปลือยเปล่าไม่ใช่สิ่งที่ถูกต้อง
Exception
อย่างแน่นอน ดูคำตอบที่ยอดเยี่ยมของ Aaron Hallแทน
ไม่สามารถรับ pythonic ได้มากกว่านี้:
raise Exception("I know python!")
ดูเอกสารการยกคำสั่งสำหรับไพ ธ อนหากคุณต้องการข้อมูลเพิ่มเติม
ใน Python3 มี 4 รูปแบบที่แตกต่างกันสำหรับการยกเว้นยกเว้น:
1. raise exception
2. raise exception (args)
3. raise
4. raise exception (args) from original_exception
1. ยกข้อยกเว้นเทียบกับ 2. ยกข้อยกเว้น (args)
หากคุณใช้raise exception (args)
เพื่อเพิ่มข้อยกเว้นแล้ว args
จะถูกพิมพ์เมื่อคุณพิมพ์วัตถุยกเว้น - ดังแสดงในตัวอย่างด้านล่าง
#raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print ("Error", exp) # Output -> Error I have raised an Exception
#raise execption
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
3.raise
raise
คำสั่งโดยไม่มีข้อโต้แย้งใด ๆ ยกข้อยกเว้นล่าสุด สิ่งนี้มีประโยชน์หากคุณจำเป็นต้องดำเนินการบางอย่างหลังจากตรวจพบข้อยกเว้นแล้วต้องการเพิ่มใหม่อีกครั้ง แต่ถ้าไม่มีข้อยกเว้นมาก่อนraise
คำสั่งจะเพิ่ม TypeError
ข้อยกเว้น
def somefunction():
print("some cleaning")
a=10
b=0
result=None
try:
result=a/b
print(result)
except Exception: #Output ->
somefunction() #some cleaning
raise #Traceback (most recent call last):
#File "python", line 8, in <module>
#ZeroDivisionError: division by zero
4. ยกข้อยกเว้น (args) จาก original_exception
คำสั่งนี้ใช้เพื่อสร้างการเชื่อมโยงข้อยกเว้นที่มีข้อยกเว้นที่เพิ่มขึ้นในการตอบสนองต่อข้อยกเว้นอื่นสามารถมีรายละเอียดของข้อยกเว้นเดิม - ดังแสดงในตัวอย่างด้านล่าง
class MyCustomException(Exception):
pass
a=10
b=0
reuslt=None
try:
try:
result=a/b
except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp
except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)
เอาท์พุท:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero
exception(args)
มากกว่าexception (args)
raise exception(args) from None
การกล่าวว่ามีการจัดการข้อยกเว้นที่ใช้งานอยู่ในปัจจุบันและไม่มีความสนใจอีกต่อไป มิฉะนั้นถ้าคุณยกข้อยกเว้นภายในexcept
บล็อกและมันไม่ได้ถูกจัดการการสืบค้นกลับสำหรับข้อยกเว้นทั้งสองจะแสดงแยกจากกันด้วยข้อความ“ ในระหว่างการจัดการกับข้อยกเว้นข้างต้นจะมีข้อยกเว้นอื่นเกิดขึ้น”
สำหรับกรณีทั่วไปที่คุณต้องส่งข้อยกเว้นเพื่อตอบสนองต่อเงื่อนไขที่ไม่คาดคิดและคุณไม่เคยตั้งใจจะจับ แต่เพียงแค่ล้มเหลวอย่างรวดเร็วเพื่อให้คุณสามารถดีบักได้จากที่นั่นหากเกิดขึ้น - ตรรกะส่วนใหญ่น่าจะเป็นAssertionError
:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
ValueError
กว่าAssertionError
เพราะไม่มีปัญหากับการยืนยัน (เพราะไม่มีการทำที่นี่) - ปัญหานั้นมีค่า หากคุณต้องการจริงๆในกรณีนี้การเขียนAssertionError
assert distance > 0, 'Distance must be positive'
แต่คุณไม่ควรตรวจสอบข้อผิดพลาดด้วยวิธีนี้เพราะสามารถปิดการยืนยันได้ ( python -O
)
-O
ที่ฉันจะทำให้โปรแกรมเพียงเพื่อนร่วมงานของฉันและบอกพวกเขาว่าพวกเขาใช้มันที่มีความเสี่ยงของตัวเองของพวกเขาหากพวกเขาทำงานด้วย
อ่านคำตอบที่มีอยู่ก่อนนี่เป็นเพียงข้อมูลเพิ่มเติม
ขอให้สังเกตว่าคุณสามารถเพิ่มข้อยกเว้นโดยมีหรือไม่มีข้อโต้แย้ง
ตัวอย่าง:
raise SystemExit
ออกจากโปรแกรม แต่คุณอาจต้องการรู้ว่าเกิดอะไรขึ้นคุณสามารถใช้สิ่งนี้ได้
raise SystemExit("program exited")
สิ่งนี้จะพิมพ์ "โปรแกรมออก" ไปยัง stderr ก่อนปิดโปรแกรม
raise SystemExit()
เป็นทางเลือกที่ดีกว่าใช่ไหม ทำไมคนแรกถึงได้ผล?
assert
วิธีการโยนข้อยกเว้นอีกประการหนึ่งคือ AssertionError
คุณสามารถใช้ยืนยันในการตรวจสอบสภาพที่จะถูกเติมเต็มหากไม่ได้แล้วมันจะเพิ่ม สำหรับรายละเอียดเพิ่มเติมดูได้ที่นี่
def avg(marks):
assert len(marks) != 0,"List is empty."
return sum(marks)/len(marks)
mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))
mark1 = []
print("Average of mark1:",avg(mark1))
ที่ควรทราบ: มีบางครั้งที่คุณต้องการจัดการข้อยกเว้นทั่วไป หากคุณกำลังประมวลผลไฟล์จำนวนมากและบันทึกข้อผิดพลาดของคุณคุณอาจต้องการตรวจจับข้อผิดพลาดที่เกิดขึ้นกับไฟล์บันทึกและดำเนินการกับไฟล์ที่เหลือต่อไป ในกรณีนั้นก
try:
foo()
except Exception as e:
print(str(e)) # Print out handled error
บล็อกวิธีที่ดีในการทำ คุณจะยังคงต้องการraise
ข้อยกเว้นเฉพาะเพื่อให้คุณรู้ว่ามันหมายถึงอะไร
คุณควรเรียนรู้คำสั่งเพิ่มของ python ควรเก็บไว้ในบล็อกลอง ตัวอย่าง -
try:
raise TypeError #remove TypeError by any other error if you want
except TypeError:
print('TypeError raised')
raise
คือสิ่งที่ฉันต้องการเพื่อให้สามารถทำการดีบักข้อผิดพลาดที่กำหนดเองได้หลายระดับของการเรียกใช้โค้ดโดยไม่ทำให้การติดตามสแต็กแตก