Pertanyaan Cara yang tepat untuk mendeklarasikan pengecualian khusus dalam Python modern?


Apa cara yang tepat untuk mendeklarasikan kelas pengecualian khusus dalam Python modern? Tujuan utama saya adalah mengikuti standar apa pun yang dimiliki kelas pengecualian lainnya, sehingga (misalnya) setiap string tambahan yang saya sertakan dalam pengecualian dicetak oleh alat apa pun yang menangkap pengecualian.

Dengan "Python modern" maksud saya sesuatu yang akan berjalan di Python 2.5 tetapi menjadi 'benar' untuk Python 2.6 dan Python 3. * cara melakukan sesuatu. Dan dengan "custom" yang saya maksud objek Exception yang dapat mencakup data tambahan tentang penyebab kesalahan: string, mungkin juga beberapa objek arbitrer lain yang relevan dengan pengecualian.

Saya tersandung oleh peringatan penghentian berikut di 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

Sepertinya itu gila BaseException memiliki arti khusus untuk atribut bernama message. Saya mengumpulkan dari PEP-352 atribut itu memang memiliki arti khusus di 2.5 mereka mencoba untuk membuang jauh, jadi saya kira nama itu (dan itu saja) sekarang dilarang? Ugh

Saya juga tidak sadar akan hal itu Exception memiliki beberapa parameter ajaib args, tapi saya tidak pernah tahu cara menggunakannya. Saya juga tidak yakin itu adalah cara yang tepat untuk melakukan hal-hal ke depan; Banyak diskusi yang saya temukan online menyarankan mereka mencoba untuk menyingkirkan argumen dengan Python 3.

Pembaruan: dua jawaban menyarankan untuk dikesampingkan __init__, dan __str__/__unicode__/__repr__. Itu sepertinya banyak mengetik, apakah itu perlu?


892
2017-08-23 21:29


asal


Jawaban:


Mungkin saya melewatkan pertanyaan itu, tetapi mengapa tidak:

class MyException(Exception):
    pass

Edit: untuk mengganti sesuatu (atau meneruskan argumen ekstra), lakukan ini:

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

Dengan cara itu Anda bisa menyampaikan pesan kesalahan ke param kedua, dan kemudian melakukannya nanti e.errors


Pembaruan Python 3: Dengan Python 3+, Anda dapat menggunakan penggunaan yang sedikit lebih ringkas ini 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

924
2017-08-23 21:55



Dengan Pengecualian Python modern, Anda tidak perlu menyalahgunakan .message, atau menimpa .__str__() atau .__repr__() atau semua itu. Jika semua yang Anda inginkan adalah pesan informatif ketika pengecualian Anda dinaikkan, lakukan ini:

class MyException(Exception):
    pass

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

Itu akan memberikan akhir yang traceback dengan MyException: My hovercraft is full of eels.

Jika Anda menginginkan lebih banyak fleksibilitas dari pengecualian, Anda bisa menyampaikan kamus sebagai argumen:

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

Namun, untuk mendapatkan detail tersebut dalam except blok sedikit lebih rumit. Detail disimpan di args atribut, yang merupakan daftar. Anda perlu melakukan sesuatu seperti ini:

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

Masih mungkin untuk memasukkan beberapa item ke pengecualian dan mengaksesnya melalui indeks tupel, tapi ini sangat putus asa (dan bahkan dimaksudkan untuk depresiasi beberapa waktu lalu). Jika Anda memang membutuhkan lebih dari satu informasi dan metode di atas tidak cukup untuk Anda, maka Anda harus subclass Exception seperti yang dijelaskan di tutorial.

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

359
2018-04-22 18:18



"Cara yang tepat untuk mendeklarasikan pengecualian khusus dalam Python modern?"

Ini bagus, kecuali pengecualian Anda adalah jenis pengecualian yang lebih spesifik:

class MyException(Exception):
    pass

Atau lebih baik (mungkin sempurna), bukan pass berikan docstring:

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

Subclassing Exclassing Subclassing

Dari dokumen

Exception

Semua pengecualian yang ada di luar sistem dan tidak ada sistem berasal dari kelas ini.   Semua pengecualian yang ditentukan pengguna juga harus berasal dari ini   kelas.

Itu artinya itu jika pengecualian Anda adalah jenis pengecualian yang lebih spesifik, subkelas kecuali yang generik Exception (dan hasilnya adalah Anda masih berasal Exception seperti yang direkomendasikan dokter). Juga, Anda setidaknya dapat memberikan docstring (dan tidak dipaksa untuk menggunakan pass kata kunci):

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

Atur atribut yang Anda buat sendiri dengan custom __init__. Hindari mengirimkan perintah sebagai argumen posisi, pengguna kode Anda di masa mendatang akan berterima kasih. Jika Anda menggunakan atribut pesan yang ditinggalkan, menugaskannya sendiri akan menghindari 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) 

Benar-benar tidak perlu menulis sendiri __str__ atau __repr__. Yang builtin sangat bagus, dan Anda pewarisan kooperatif memastikan bahwa Anda menggunakannya.

Kritik atas jawaban atas

Mungkin saya melewatkan pertanyaan itu, tetapi mengapa tidak:

class MyException(Exception):
    pass

Sekali lagi, masalah di atas adalah bahwa untuk menangkapnya, Anda harus menyebutkannya secara khusus (mengimpornya jika dibuat di tempat lain) atau menangkap Pengecualian, (tetapi Anda mungkin tidak siap untuk menangani semua jenis Pengecualian, dan Anda hanya perlu menangkap pengecualian yang siap Anda tangani). Kritik serupa ke bawah, tetapi selain itu bukan cara untuk menginisialisasi melalui super, dan Anda akan mendapatkan DeprecationWarning jika Anda mengakses atribut pesan:

Edit: untuk mengganti sesuatu (atau meneruskan argumen ekstra), lakukan ini:

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

Dengan cara itu Anda bisa menyampaikan pesan kesalahan ke param kedua, dan sampai nanti dengan e.errors

Ini juga membutuhkan dua argumen untuk dilewatkan (selain dari self.) Tidak lebih, tidak kurang. Itu adalah kendala yang menarik bahwa pengguna masa depan mungkin tidak menghargai.

Untuk langsung - itu melanggar Substitusi Liskov.

Saya akan menunjukkan kedua kesalahan:

>>> 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'

Dibandingkan dengan:

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

150
2017-11-14 21:09



lihat bagaimana pengecualian bekerja secara default jika salah satunya vs lebih banyak atribut digunakan (tracebacks dihilangkan):

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

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

jadi Anda mungkin ingin memiliki semacam "templat pengecualian", berfungsi sebagai pengecualian itu sendiri, dengan cara yang kompatibel:

>>> 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')

ini dapat dilakukan dengan mudah dengan subkelas ini

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

dan jika Anda tidak menyukai representasi tupel seperti biasa, tambahkan saja __str__ metode ke ExceptionTemplate kelas, seperti:

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

dan Anda akan memilikinya

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

37
2017-08-07 16:23



Anda harus mengesampingkan __repr__ atau __unicode__ metode daripada menggunakan pesan, argumen yang Anda berikan saat Anda membangun pengecualian akan berada di args atribut objek pengecualian.


14
2017-08-23 21:46



Tidak, "pesan" tidak terlarang. Itu tidak lagi digunakan. Aplikasi Anda akan berfungsi dengan baik menggunakan pesan. Tetapi Anda mungkin ingin menyingkirkan kesalahan depresiasi, tentu saja.

Ketika Anda membuat kelas Eksepsi kustom untuk aplikasi Anda, banyak dari mereka tidak subkelas hanya dari Exception, tetapi dari yang lain, seperti ValueError atau serupa. Maka Anda harus beradaptasi dengan penggunaan variabel mereka.

Dan jika Anda memiliki banyak pengecualian dalam aplikasi Anda, itu biasanya ide yang baik untuk memiliki kelas dasar umum yang sama untuk semua dari mereka, sehingga pengguna modul Anda dapat melakukan

try:
    ...
except NelsonsExceptions:
    ...

Dan dalam hal ini Anda bisa melakukan itu __init__ and __str__ diperlukan di sana, jadi Anda tidak perlu mengulanginya untuk setiap pengecualian. Tetapi hanya memanggil variabel pesan sesuatu yang lain dari pesan yang melakukan trik.

Dalam hal apapun, Anda hanya membutuhkan __init__ or __str__ jika Anda melakukan sesuatu yang berbeda dari apa yang Exception sendiri lakukan. Dan karena jika penghentian, Anda perlu keduanya, atau Anda mendapatkan kesalahan. Itu tidak banyak kode tambahan yang Anda butuhkan per kelas. ;)


5
2017-08-23 21:58



Coba Contoh ini

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")

0
2017-07-22 05:27