最新の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

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

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

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

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

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

2021 年 6 月 8 日にニューヨーク市で開催された covid-19 パンデミックで亡くなった人々の命を偲び、祝うために、ネーミング ザ ロスト メモリアルズが主催するイベントと行進の最中に、グリーンウッド墓地の正門から記念碑がぶら下がっています。週末、ジョー・バイデン大統領は、covid-19 パンデミックの終息を宣言しました。これは、過去 2 年以上にわたり、公の場でそうするための長い列の中で最新のものです。

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

オハイオ州のエミリー・デシャネル みんな早く来て、ボーンズが帰ってきた!まあ、ショーボーンズではなく、彼女を演じた俳優. エミリー・デシャネルに最後に会ってからしばらく経ちました.Emily Deschanel は、長期にわたるプロシージャルな Bones の Temperance “Bones” Brennan としてよく知られています。

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

どうやら、ドナルド・トランプに近い人々は、今月初めにFBIによって家宅捜索された彼のMar-a-Lago財産からの映像を公開するよう彼に勧めています. 前大統領はテープを公開するかどうかを確認していませんが、息子はフォックス・ニュースにそうなるだろうと語った.

Andor は、他の Star Wars ショーから大きな距離を置きます。

Andor は、他の Star Wars ショーから大きな距離を置きます。

アンドールの一場面。数十年前、ジョージ・ルーカスがスター・ウォーズのテレビ番組を制作するのを妨げた主な理由は、お金でした。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language