最新のPythonでカスタム例外を宣言する適切な方法は?

1373
Nelson 2009-08-24 11:29.

最新のPythonでカスタム例外クラスを宣言する適切な方法は何ですか?私の主な目標は、他の標準の例外クラスに準拠することです。これにより、(たとえば)例外に含める余分な文字列は、例外をキャッチしたツールによって出力されます。

「最新のPython」とは、Python 2.5で実行されるが、Python2.6およびPython3。*の方法では「正しい」ものを意味します。また、「カスタム」とは、エラーの原因に関する追加データを含めることができるExceptionオブジェクトを意味します。文字列、場合によっては例外に関連するその他の任意のオブジェクトです。

Python2.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、使い方がわかりません。また、これが今後の正しい方法であるかどうかもわかりません。私がオンラインで見つけた多くの議論は、彼らがPython3で引数を廃止しようとしていることを示唆していました。

更新:2つの答えが上書き示唆している__init__、および__str__/ __unicode__/ __repr__。それは多くのタイピングのようです、それは必要ですか?

11 answers

1414
gahooa 2009-08-24 11:55.

たぶん私は質問を逃したかもしれませんが、なぜそうではありません:

class MyException(Exception):
    pass

編集:何かをオーバーライドする(または余分な引数を渡す)には、次のようにします。

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

そうすれば、エラーメッセージのdictを2番目のパラメータに渡し、後でそれを取得できます。 e.errors


Python 3アップデート: 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
534
frnknstn 2012-04-23 08:18.

最新のPython例外を使用すると、悪用.messageしたり、オーバーライドし.__str__()たり、.__repr__()あるいはそのいずれかを行う必要はありません。例外が発生したときに情報メッセージだけが必要な場合は、次のようにします。

class MyException(Exception):
    pass

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

これにより、で終わるトレースバックが得られ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"])

複数のアイテムを例外に渡し、タプルインデックスを介してそれらにアクセスすることは引き続き可能ですが、これは非常に推奨されていません(また、しばらく前に非推奨にすることを目的としていました)。複数の情報が必要で、上記の方法では不十分な場合Exceptionは、チュートリアルの説明に従ってサブクラス化する必要があります。

class MyError(Exception):
    def __init__(self, message, animal):
        self.message = message
        self.animal = animal
    def __str__(self):
        return self.message
207
Aaron Hall 2014-11-15 11:09.

「最新のPythonでカスタム例外を宣言する適切な方法は?」

例外が実際にはより具体的な例外の一種でない限り、これは問題ありません。

class MyException(Exception):
    pass

またはpass、docstringを与える代わりに、より良い(おそらく完璧):

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

例外サブクラスのサブクラス化

ドキュメントから

Exception

すべての組み込みの、システムを終了しない例外は、このクラスから派生します。すべてのユーザー定義の例外も、このクラスから派生する必要があります。

つまり、例外がより具体的な例外のタイプである場合は、ジェネリックではなくその例外をサブクラス化しますException(その結果Exception、ドキュメントで推奨されているように、引き続き派生します)。また、少なくともdocstringを指定できます(passキーワードの使用を強制されません)。

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

カスタムで自分で作成した属性を設定します__init__。dictを位置引数として渡すことは避けてください。コードの将来のユーザーはあなたに感謝します。非推奨のメッセージ属性を使用する場合は、自分で割り当てることで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と次のようになります。

編集:何かをオーバーライドする(または余分な引数を渡す)には、次のようにします。

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

そうすれば、エラーメッセージのdictを2番目のパラメータに渡し、後でe.errorsを使用してそれに到達することができます。

また、(self。を除いて)正確に2つの引数を渡す必要があります。これ以上でもそれ以下でもありません。これは、将来のユーザーが理解できないかもしれない興味深い制約です。

直接的なこと-それはリスコフの置換可能性に違反します。

両方のエラーを示します。

>>> 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'
53
mykhal 2013-08-08 06:23.

1場合は、例外はデフォルトで動作を確認VS以上の属性が使用されている(トレースバックは省略):

>>> 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
40
fameman 2018-11-26 07:14.

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で頻繁に使用されますが、フレームワークを使用していて、考えられるすべてのエラーをキャッチする必要がある場合は、煩わしいことがあります。

18
M. Utku ALTINKAYA 2009-08-24 11:46.

メッセージを使用する代わりに、__repr__または__unicode__メソッドをオーバーライドする必要がargsあります。例外を作成するときに指定する引数は、例外オブジェクトの属性に含まれます。

14
Eugene Yarmash 2020-03-01 03:02.

独自の例外を正しく定義するには、従う必要のあるいくつかのベストプラクティスがあります。

  • から継承する基本クラスを定義し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().__init__(*args)
            self.foo = kwargs.get('foo')
    

    追加の引数を使用してこのような例外を発生させるには、次を使用できます。

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

この設計は、基本例外クラスのインスタンスを派生例外クラスのインスタンスに置き換えることができるため、リスコフの置換原則に準拠しています。また、親と同じパラメーターを使用して派生クラスのインスタンスを作成することもできます。

9
Lennart Regebro 2009-08-24 11:58.

いいえ、「メッセージ」は禁止されていません。非推奨になりました。アプリケーションはメッセージを使用して正常に動作します。ただし、もちろん、非推奨エラーを取り除きたい場合もあります。

アプリケーションのカスタムExceptionクラスを作成する場合、それらの多くはExceptionだけでなく、ValueErrorなどの他のクラスからもサブクラス化されます。次に、変数の使用法に適応する必要があります。

また、アプリケーションに多くの例外がある場合は、通常、モジュールのユーザーが実行できるように、それらすべてに共通のカスタム基本クラスを用意することをお勧めします。

try:
    ...
except NelsonsExceptions:
    ...

その場合__init__ and __str__、そこで必要なことを行うことができるので、すべての例外に対してそれを繰り返す必要はありません。しかし、メッセージ変数をメッセージ以外の何かを呼び出すだけでうまくいきます。

いずれにせよ、__init__ or __str__例外自体が行うこととは異なることを行う場合にのみ必要です。そして、非推奨の場合は、両方が必要になるか、エラーが発生するためです。これは、クラスごとに必要な追加のコードの多くではありません。;)

8
Yaroslav Nikitenko 2019-06-11 10:40.

非常に優れた記事「Python例外の決定的なガイド」を参照してください。基本原則は次のとおりです。

  • 常に(少なくとも)例外から継承します。
  • 常にBaseException.__init__1つの引数のみで呼び出します。
  • ライブラリを構築するときは、Exceptionから継承する基本クラスを定義します。
  • エラーに関する詳細を提供します。
  • 必要に応じて、組み込みの例外タイプから継承します。

例外の整理(モジュール内)とラッピングに関する情報もあります。ガイドを読むことをお勧めします。

4
omkaartg 2018-07-22 19:27.

この例を試してください

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")
3
Macintosh Fan 2020-07-03 10:37.

本当に簡単なアプローチ:

class CustomError(Exception):
    pass

raise CustomError("Hmm, seems like this was custom coded...")

または、印刷せずにエラーを発生させます__main__(きれいに見える場合があります)。

class CustomError(Exception):
    __module__ = Exception.__module__

raise CustomError("Improved CustomError!")

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

ライアン・フェルトンがジャロプニックを去る。アメリカの詐欺と嘘つきは安堵のため息をつく。スタッフが再び昼食時に肉を注文する

ライアン・フェルトンがジャロプニックを去る。アメリカの詐欺と嘘つきは安堵のため息をつく。スタッフが再び昼食時に肉を注文する

「あなたは知っているべきです」とライアン・フェルトンは電話にひそかに言いました、「私は本当にできないこと。

今日だけMovadoウォッチが50%オフで、手首にちょっとした贅沢を加えましょう

今日だけMovadoウォッチが50%オフで、手首にちょっとした贅沢を加えましょう

ノードストロームラックのMovadoイベントMovadoは、クラシックで象徴的な時計ブランドですが、価格が1,000ドルを超えるため、ほとんどの人にとって信じられないほど手に負えないと感じる可能性があります。しかし、ノードストロームラックのMovadoイベントを使用すると、小売価格の半分でゴージャスな時計を獲得できます。いくつかのスタイルは400ドル未満で提供されます。

L7は18年ぶりのショーをプレイしました

L7は18年ぶりのショーをプレイしました

これは素晴らしいことです。L7のオリジナルラインナップは18年ぶりに再会し、LAのエコーで完売したショーを演奏しました。

「またの名をグレイス」は、見られているという不穏な感覚を体現しています。

「またの名をグレイス」は、見られているという不穏な感覚を体現しています。

「またの名をグレイス」ジョーダン博士は、19世紀半ばのカナダの性別の役割と階級の違いが、グレイスの心を解き放とうとし続けています。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

エッジ機能とは?!?

エッジ機能とは?!?

エッジ機能…それはセクシーに聞こえます、あなたのおばあちゃんはそれが何であるかを知らないでしょう、そしてあなたはおそらくまだどちらも知らないでしょう。それは大丈夫です、うまくいけば、私はより簡単な方法でエッジを理解するためのあなたの旅であなたを助けるためにここにいます。

また、より顧客重視を「試み」ていますか?

また、より顧客重視を「試み」ていますか?

顧客が好きなものから、顧客が好き/嫌いなものについて利害関係者の内臓が言うものに焦点が移る製品機能の議論を経験したことがありますか?チームの全員がお互いに何をすべきかを尋ねていて、誰もその製品に興味を持っていないように見える時期を経験したことがありますか?フィードバックループフィードバック—否定的な意味合いがありますよね?実際には、フィードバックは環境から私たちの行動への反応であり、私たちに利益をもたらすためにそれを使用するには、この反応を分析する必要があります。通常、これには従いません。

あなたがアメリカを怖がっているなら、あなたはまだ十分に怖がっていません

あなたがアメリカを怖がっているなら、あなたはまだ十分に怖がっていません

神学者とファシストは全体主義社会を望んでいます—そして彼らは世界中で1つの衝撃波を反響させようとしています。友人や人々でいっぱいの古いヨーロッパの古い街にある私の小さなカフェで、「彼らは本当にそれをしましたか?」「彼らはクレイジーですか?」「これからどうなるの?」"我が神よ!" 角を曲がったところにある公園で、犬たちは幸せな夏の輪の中で回転します。「信じられない!」「アメリカ人にできることはありますか?」"今、何が起きた?" 今、何が起きた?それがみんなの頭の中にある質問です。

私の技術の旅

速いお金があなたが望むものであるか、ストレスのない生活があなたが切望するものであるならば、私はあなたに技術があなたのものではないことを断固として言いたいです。私を信じて。

Language