วิธีที่เหมาะสมในการประกาศข้อยกเว้นที่กำหนดเองใน Python สมัยใหม่หรือไม่


1289

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

โดย "modern Python" ฉันหมายถึงสิ่งที่จะทำงานใน Python 2.5 แต่จะ 'ถูกต้อง' สำหรับ Python 2.6 และ Python 3 * วิธีการทำสิ่งต่างๆ และโดย "กำหนดเอง" ฉันหมายถึงวัตถุยกเว้นที่สามารถรวมข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุของข้อผิดพลาด: สตริงหรืออาจเป็นวัตถุอื่น ๆ ที่เกี่ยวข้องกับข้อยกเว้น

ฉันถูกสะดุดโดยคำเตือนเรื่องเลิกใช้งานต่อไปนี้ใน Python 2.6.2:

>>> class MyError(Exception):
...     def __init__(self, message):
...         self.message = message
... 
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6

ดูเหมือนว่าบ้าที่มีความหมายพิเศษสำหรับแอตทริบิวต์ชื่อBaseException messageฉันรวบรวมจากPEP-352ที่คุณลักษณะนั้นมีความหมายพิเศษใน 2.5 พวกเขาพยายามที่จะเลิกใช้ดังนั้นฉันเดาว่าชื่อ (และชื่อเดียว) นั้นถูกห้ามตอนนี้หรือไม่ ฮึ.

ฉันเองก็สับสนด้วยว่าExceptionมีพารามิเตอร์เวทย์มนตร์บ้างargsแต่ฉันไม่เคยรู้จักวิธีใช้มัน ฉันไม่แน่ใจว่ามันเป็นวิธีที่ถูกต้องในการทำสิ่งต่าง ๆ ในอนาคต การสนทนาจำนวนมากที่ฉันพบออนไลน์แนะนำให้พวกเขาพยายามทำสิ่งต่าง ๆ ใน Python 3

ปรับปรุง: สองคำตอบมีข้อเสนอแนะเอาชนะ__init__และ__str__/ /__unicode__ __repr__ดูเหมือนว่าจะมีการพิมพ์จำนวนมากจำเป็นไหม?

คำตอบ:


1322

บางทีฉันอาจพลาดคำถาม แต่ทำไมไม่:

class MyException(Exception):
    pass

แก้ไข:เพื่อแทนที่บางสิ่ง (หรือผ่าน args พิเศษ) ทำสิ่งนี้:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

ด้วยวิธีนี้คุณสามารถส่งผ่านข้อความแสดงข้อผิดพลาดไปยังพารามิเตอร์ที่สองและไปหามันในภายหลังด้วย e.errors


Python 3 Update:ใน Python 3+ คุณสามารถใช้การใช้งานที่กะทัดรัดกว่านี้ได้เล็กน้อยsuper():

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super().__init__(message)

        # Now for your custom code...
        self.errors = errors

35
อย่างไรก็ตามข้อยกเว้นที่กำหนดเช่นนี้จะไม่สามารถเลือกได้ ดูการสนทนาที่นี่stackoverflow.com/questions/16244923/…
jiakai

86
@jiakai หมายถึง "picklable" :-)
Robino

1
การปฏิบัติตามเอกสารของไพ ธ อนสำหรับข้อยกเว้นที่ผู้ใช้กำหนดชื่อที่กล่าวถึงในฟังก์ชั่น __init__ นั้นไม่ถูกต้อง แทนที่จะเป็น (self, message, error) มันคือ (self, expression, message) การแสดงออกของคุณลักษณะคือการแสดงออกของการป้อนข้อมูลที่เกิดข้อผิดพลาดและข้อความเป็นคำอธิบายของข้อผิดพลาด
ddleon

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

498

มีข้อยกเว้นงูหลามที่ทันสมัยคุณไม่จำเป็นต้องมีการละเมิด.messageหรือการแทนที่.__str__()หรือ.__repr__()หรือใด ๆ ของมัน หากสิ่งที่คุณต้องการคือข้อความแจ้งข้อมูลเมื่อมีการยกข้อยกเว้นของคุณให้ทำสิ่งนี้:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

ที่จะให้ traceback MyException: My hovercraft is full of eelsลงท้ายด้วย

หากคุณต้องการความยืดหยุ่นมากขึ้นจากข้อยกเว้นคุณสามารถส่งพจนานุกรมเป็นอาร์กิวเมนต์:

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

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

try:
    raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
    details = e.args[0]
    print(details["animal"])

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

class MyError(Exception):
    def __init__(self, message, animal):
        self.message = message
        self.animal = animal
    def __str__(self):
        return self.message

2
"แต่สิ่งนี้จะถูกยกเลิกในอนาคต" - สิ่งนี้ยังคงมีไว้สำหรับการคัดค้านหรือไม่? งูหลาม 3.7 Exception(foo, bar, qux)ยังดูเหมือนว่าจะมีความสุขยอมรับ
mtraceur

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

@frnknstn ทำไมถึงหมดกำลังใจ? ดูเหมือนสำนวนที่ดีสำหรับฉัน
neves

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

ส่วนที่เกี่ยวข้องของ PEP352 คือ"ไอเดียหด"
ปลดแอก

196

"วิธีการที่เหมาะสมในการประกาศข้อยกเว้นที่กำหนดเองใน Python สมัยใหม่?"

ไม่เป็นไรเว้นแต่ว่าข้อยกเว้นของคุณจะเป็นข้อยกเว้นที่เฉพาะเจาะจงมากขึ้น:

class MyException(Exception):
    pass

หรือดีกว่า (อาจสมบูรณ์แบบ) แทนที่จะpassให้ docstring:

class MyException(Exception):
    """Raise for my specific kind of exception"""

คลาสย่อยยกเว้นข้อยกเว้น

จากเอกสาร

Exception

ข้อยกเว้นในตัวทั้งหมดที่ไม่ได้ออกจากระบบมาจากคลาสนี้ ข้อยกเว้นที่ผู้ใช้กำหนดเองควรได้รับมาจากคลาสนี้ด้วย

นั่นหมายความว่าถ้าข้อยกเว้นของคุณเป็นประเภทของข้อยกเว้นเฉพาะเจาะจงมากขึ้น subclass ที่ยกเว้นแทนทั่วไปException(และผลที่ได้จะเป็นไปได้ว่าคุณยังคงได้รับจากการExceptionเป็นเอกสารแนะนำ) นอกจากนี้คุณอย่างน้อยสามารถให้ docstring (และไม่ถูกบังคับให้ใช้passคำหลัก):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

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

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

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

คำติชมของคำตอบด้านบน

บางทีฉันอาจพลาดคำถาม แต่ทำไมไม่:

class MyException(Exception):
    pass

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

แก้ไข: เพื่อแทนที่บางสิ่ง (หรือผ่าน args พิเศษ) ทำสิ่งนี้:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

ด้วยวิธีนี้คุณสามารถส่งผ่านข้อความแสดงข้อผิดพลาดไปยังพารามิเตอร์ที่สองและไปที่ e.errors ในภายหลัง

นอกจากนี้ยังต้องมีการส่งผ่านข้อโต้แย้งสองประการ (นอกเหนือจากself.) ไม่มากไม่น้อย นั่นเป็นข้อ จำกัด ที่น่าสนใจที่ผู้ใช้ในอนาคตอาจไม่พอใจ

ที่จะตรง - มันละเมิด Liskov ทดแทน

ฉันจะแสดงข้อผิดพลาดทั้งสอง:

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

เปรียบเทียบกับ:

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'

2
สวัสดีปี 2018! BaseException.messageมันหายไปแล้วใน Python 3 ดังนั้นคำวิจารณ์จึงมีเฉพาะสำหรับรุ่นเก่าใช่ไหม
Kos

5
@Kos คำวิจารณ์เกี่ยวกับการทดแทน Liskov ยังคงใช้ได้ ความหมายของการโต้แย้งครั้งแรกในฐานะ "ข้อความ" นั้นเป็นที่ถกเถียงกันอยู่เช่นกัน แต่ฉันไม่คิดว่าฉันจะโต้แย้งประเด็นนี้ ฉันจะดูนี้มากขึ้นเมื่อฉันมีเวลาว่างมากขึ้น
Aaron Hall

1
FWIW สำหรับ Python 3 (อย่างน้อย 3.6+) จะกำหนด__str__วิธีการMyAppValueErrorแทนการพึ่งพาmessageแอตทริบิวต์
Jacquot

1
@AaronHall คุณสามารถขยายผลประโยชน์ของ ValueError การจัดกลุ่มย่อยมากกว่าข้อยกเว้นได้หรือไม่ คุณระบุว่านี่คือสิ่งที่มีความหมายโดยเอกสาร แต่การอ่านโดยตรงไม่สนับสนุนการตีความนั้นและใน Python Tutorial ภายใต้ข้อยกเว้นที่ผู้ใช้กำหนดเองทำให้ผู้ใช้เลือกอย่างชัดเจน: "โดยปกติแล้วข้อยกเว้นควรมาจากคลาส Exception ไม่ว่าโดยตรงหรือโดยอ้อม " ดังนั้นอยากเข้าใจว่ามุมมองของคุณสมเหตุสมผลหรือไม่
ostergaard

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

50

ดูวิธีการทำงานข้อยกเว้นโดยปริยายหากเทียบกับคุณลักษณะอื่น ๆ ที่มีการใช้ (tracebacks ละเว้น):

>>> raise Exception('bad thing happened')
Exception: bad thing happened

>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')

ดังนั้นคุณอาจต้องการมี " แม่แบบยกเว้น " ซึ่งทำงานเป็นข้อยกเว้นในลักษณะที่เข้ากันได้:

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')

สิ่งนี้สามารถทำได้อย่างง่ายดายด้วยคลาสย่อยนี้

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass

และถ้าคุณไม่ชอบการแทนค่าแบบทูเปิลที่เป็นค่าเริ่มต้นเพียงเพิ่ม__str__เมธอดให้กับExceptionTemplateคลาสเช่น:

    # ...
    def __str__(self):
        return ': '.join(self.args)

และคุณจะมี

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken

32

ตั้งแต่ Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html ) วิธีการที่แนะนำยังคง:

class CustomExceptionName(Exception):
    """Exception raised when very uncommon things happen"""
    pass

กรุณาอย่าลืมเอกสารทำไมข้อยกเว้นที่กำหนดเองจึงเป็นสิ่งจำเป็น!

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

class CustomExceptionName(Exception):
    """Still an exception raised when uncommon things happen"""
    def __init__(self, message, payload=None):
        self.message = message
        self.payload = payload # you could add more args
    def __str__(self):
        return str(self.message) # __str__() obviously expects a string to be returned, so make sure not to send any other data types

และดึงพวกเขาเช่น:

try:
    raise CustomExceptionName("Very bad mistake.", "Forgot upgrading from Python 1")
except CustomExceptionName as error:
    print(str(error)) # Very bad mistake
    print("Detail: {}".format(error.payload)) # Detail: Forgot upgrading from Python 1

payload=Noneเป็นสิ่งสำคัญที่จะทำให้ดองได้ error.__reduce__()ก่อนที่จะทิ้งมันคุณมีการโทร กำลังโหลดจะทำงานตามที่คาดไว้

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


1
อย่างน้อยที่สุดเอกสารปัจจุบันระบุว่านี่เป็นวิธีที่จะทำได้ (อย่างน้อยก็ไม่มี__str__) มากกว่าคำตอบอื่น ๆ ที่ใช้super().__init__(...).. เพียงความอัปยศที่แทนที่__str__และ__repr__อาจจำเป็นสำหรับการจัดลำดับ "เริ่มต้น" ที่ดีกว่า
kevlarr

2
คำถามที่ซื่อสัตย์: ทำไมจึงเป็นสิ่งสำคัญที่ข้อยกเว้นจะดองได้? กรณีการใช้งานสำหรับการถ่ายโอนข้อมูลและการโหลดข้อยกเว้นคืออะไร
Roel Schroeven

1
@RoelSchroeven: ฉันต้องขนานรหัสครั้งเดียว เรียกใช้กระบวนการเดียวที่ละเอียดอ่อน แต่แง่มุมของบางคลาสไม่ได้เป็นอนุกรม (ฟังก์ชั่นแลมบ์ดาถูกส่งผ่านเป็นวัตถุ) เอาเวลาฉันหาและแก้ไขมัน หมายความว่าใครบางคนในภายหลังอาจจำเป็นต้องใช้รหัสของคุณในการทำให้เป็นอนุกรมไม่สามารถทำได้และต้องค้นหาสาเหตุว่าทำไม ... ปัญหาของฉันไม่ใช่ข้อผิดพลาดที่ไม่สามารถแก้ไขได้ แต่ฉันเห็นว่าเป็นสาเหตุของปัญหาที่คล้ายกัน
logicOnAbstractions

17

คุณควรแทนที่__repr__หรือ__unicode__วิธีการแทนการใช้ข้อความ args ที่คุณให้ไว้เมื่อคุณสร้างข้อยกเว้นจะอยู่ในargsแอตทริบิวต์ของวัตถุข้อยกเว้น


7

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

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

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

try:
    ...
except NelsonsExceptions:
    ...

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

ไม่ว่าในกรณีใดคุณเพียง แต่ต้อง__init__ or __str__ทำสิ่งที่แตกต่างจากข้อยกเว้น และเพราะหากการคัดค้านคุณต้องใช้ทั้งคู่หรือคุณได้รับข้อผิดพลาด นั่นไม่ใช่รหัสพิเศษจำนวนมากที่คุณต้องการต่อชั้นเรียน ;)


เป็นที่น่าสนใจว่าข้อยกเว้น Django ไม่ได้สืบทอดมาจากฐานร่วม docs.djangoproject.com/en/2.2/_modules/django/core/exceptionsคุณมีตัวอย่างที่ดีหรือไม่เมื่อมีการเรียกใช้ข้อยกเว้นทั้งหมดจากแอปพลิเคชันเฉพาะ (อาจเป็นประโยชน์สำหรับแอปพลิเคชันบางประเภทเท่านั้น)
Yaroslav Nikitenko

ผมพบว่าเป็นบทความที่ดีในหัวข้อนี้julien.danjou.info/python-exceptions-guide ฉันคิดว่าข้อยกเว้นควรได้รับการแบ่งประเภทเป็นหลักตามโดเมนไม่ใช่แอปพลิเคชัน เมื่อแอพของคุณเกี่ยวกับโปรโตคอล HTTP คุณได้รับมาจาก HTTPError เมื่อส่วนหนึ่งของแอปของคุณคือ TCP คุณจะได้รับข้อยกเว้นของส่วนนั้นจาก TCPError แต่ถ้าแอปของคุณครอบคลุมโดเมนจำนวนมาก (ไฟล์สิทธิ์ ฯลฯ ) สาเหตุที่ทำให้ MyBaseException ลดน้อยลง หรือเป็นการป้องกันจาก 'การละเมิดเลเยอร์'?
Yaroslav Nikitenko

6

ดูบทความที่ดีมาก " คู่มือที่ชัดเจนสำหรับข้อยกเว้น Python " หลักการพื้นฐานคือ:

  • รับสืบทอดจาก (อย่างน้อย) ข้อยกเว้นเสมอ
  • โทรBaseException.__init__ด้วยอาร์กิวเมนต์เดียวเท่านั้น
  • เมื่อสร้างห้องสมุดให้กำหนดคลาสพื้นฐานที่สืบทอดจาก Exception
  • ระบุรายละเอียดเกี่ยวกับข้อผิดพลาด
  • รับช่วงจากข้อยกเว้นในตัวเมื่อมันเหมาะสม

นอกจากนี้ยังมีข้อมูลเกี่ยวกับการจัดระเบียบ (ในโมดูล) และข้อยกเว้นการตัดผมแนะนำให้อ่านคู่มือ


1
นี่เป็นตัวอย่างที่ดีว่าทำไมบนดังนั้นฉันมักจะตรวจสอบคำตอบ upvoted ที่สุด แต่คนล่าสุดเช่นกัน มีประโยชน์เพิ่มเติมขอบคุณ
logicOnAbstractions

1
Always call BaseException.__init__ with only one argument.ดูเหมือนว่ามีข้อ จำกัด ที่ไม่จำเป็นเนื่องจากจริง ๆ แล้วยอมรับข้อโต้แย้งจำนวนเท่าใดก็ได้
Eugene Yarmash

@EugeneYarmash ฉันเห็นด้วยตอนนี้ฉันไม่เข้าใจ ฉันไม่ได้ใช้มันเลย บางทีฉันควรอ่านบทความอีกครั้งและขยายคำตอบของฉัน
Yaroslav Nikitenko

@EugeneYarmash ฉันอ่านบทความอีกครั้ง มีการระบุว่าในกรณีที่มีการขัดแย้งหลายครั้งการใช้งาน C เรียกว่า "return PyObject_Str (self-> args);" หมายความว่าหนึ่งสายควรทำงานได้ดีกว่าหลาย ๆ สตริง คุณตรวจสอบสิ่งนั้นหรือไม่
Yaroslav Nikitenko

3

ลองตัวอย่างนี้

class InvalidInputError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return repr(self.msg)

inp = int(input("Enter a number between 1 to 10:"))
try:
    if type(inp) != int or inp not in list(range(1,11)):
        raise InvalidInputError
except InvalidInputError:
    print("Invalid input entered")

1

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

  • Exceptionกำหนดสืบทอดชั้นฐานจาก สิ่งนี้จะช่วยให้สามารถตรวจจับข้อยกเว้นใด ๆ ที่เกี่ยวข้องกับโครงการ (ข้อยกเว้นที่เฉพาะเจาะจงมากขึ้นควรสืบทอดจากมัน)

    class MyProjectError(Exception):
        """A base class for MyProject exceptions."""

    การจัดการคลาสยกเว้นเหล่านี้ในโมดูลแยกต่างหาก (เช่นexceptions.py) เป็นความคิดที่ดี

  • ในการสร้างข้อยกเว้นที่กำหนดเองให้คลาสย่อยคลาสยกเว้นฐาน

  • หากต้องการเพิ่มการสนับสนุนสำหรับอาร์กิวเมนต์พิเศษในข้อยกเว้นที่กำหนดเองให้กำหนด__init__()วิธีการที่กำหนดเองด้วยจำนวนตัวแปรที่มีข้อโต้แย้ง เรียกคลาสพื้นฐาน__init__()โดยส่งอาร์กิวเมนต์ตำแหน่งใด ๆ ไปยังคลาสนั้น (จำไว้ว่าBaseException/Exceptionคาดว่าจะมีอาร์กิวเมนต์ตำแหน่งเท่าใดก็ได้):

    class CustomError(MyProjectError):
        def __init__(self, *args, **kwargs):
            super(CustomError, self).__init__(*args)
            self.foo = kwargs.get('foo')

    ในการเพิ่มข้อยกเว้นด้วยอาร์กิวเมนต์พิเศษคุณสามารถใช้:

    raise CustomError('Something bad happened', foo='foo')

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

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