Python:__init__内で例外を発生させるのは悪いフォームですか?


128

内で例外を発生させることは悪い形と考えられます__init__か?もしそうなら、特定のクラス変数がNone正しくない型として初期化されたときにエラーをスローするための受け入れられた方法は何ですか?


Cでは、デストラクタで例外を発生させるのは不適切ですが、コンストラクタは問題ありません。
Calyth

6
@Calyth、あなたの平均C ++ - C.にはコンストラクタやデストラクタはありません
アレックスマルテッリは

4
...またはそれについては例外。
Matt Wilding 2013

@Calyth:笑、無実のタイプミスによる無意味なステートメントを削除してください;-)
lpapp

4
@lpappはい。C ++。FML。そして、そのコメントは編集できませんでした。だからインターネットは私の愚かさを文書化するでしょう;)
Calyth

回答:


159

内部__init__()で例外を発生させることは絶対に問題ありません。コンストラクター内でエラー状態を示すための良い方法は他にありません。標準ライブラリには、オブジェクトを構築すると例外が発生する可能性のある何百もの例があります。

もちろん、発生させるエラークラスはあなた次第です。ValueErrorコンストラクターに無効なパラメーターが渡された場合に最適です。


14
コンストラクタで最もよく使用される例外はValueErrorおよびTypeErrorです。
Denis Otkidach、2009年

9
それ__init__はコンストラクタではなく、その初期化子であることを指摘するだけです。あなたは行を編集したい場合がありますコンストラクタ内でエラー状態を示すために、他の良い道が..ありません
ハリス

26

コンストラクタでエラーを示す唯一の適切な方法は、例外を発生させることです。そのため、C ++や、例外の安全性を考慮して設計された他のオブジェクト指向言語では、オブジェクトのコンストラクターで例外がスローされても、デストラクタは呼び出されません(つまり、オブジェクトの初期化が不完全です)。これは、Pythonなどのスクリプト言語には当てはまりません。たとえば、次のコードは、socket.connect()が失敗した場合にAttributeErrorをスローします。

class NetworkInterface:
    def __init__(self, address)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(address)
        self.stream = self.socket.makefile()

    def __del__(self)
        self.stream.close()
        self.socket.close()

その理由は、接続の試行が失敗した後、ストリーム属性が初期化される前に、不完全なオブジェクトのデストラクタが呼び出されるためです。コンストラクターから例外をスローすることを避けてはいけません。Pythonで完全に例外セーフなコードを書くのは難しいと言っているだけです。一部のPython開発者はデストラクタの使用を完全に避けていますが、それは別の議論の問題です。


この答えは本当に役に立ちます。「青信号」について話すだけでなく、「デストラクタ」の例についても触れています。
lpapp

あなたのpythonコード__del__はひどく書かれているので失敗します。this->p_socket->close()C ++でデストラクタを使用した場合も、まったく同じ問題が発生します。C ++では、そうはしません。メンバーオブジェクトにそれ自体を破壊させます。Pythonでも同じようにします。
エリック

1
@Eric C ++は適切に初期化されていないオブジェクトを破棄しないため、C ++では問題が発生しません。実際、C ++で非常に一般的なイディオムは、コンストラクタでリソース割り当て、デストラクタで割り当てを解除することです。メンバーオブジェクトはそれ自体を破棄することをお勧めします(デストラクタでリソースの割り当てを解除します)。その場合、メンバークラスを記述するときに同じ問題が発生します。
Seppo Enarvi 2018年

11

私はそれが悪い形であるべき理由を何も見ていません。

逆に、エラーコードを返すのとは対照的に、例外がうまく機能することで知られていることの1つは、エラーコードは通常、コンストラクターによって返されないことです。したがって、少なくともC ++のような言語では、例外を発生させることがエラーを通知する唯一の方法です。


6

標準ライブラリは言う:

>>> f = file("notexisting.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'notexisting.txt'

また、私はそれが悪い形と考えられるべき理由を本当に見ていません。


3

それは組み込みのValueError例外の完璧なケースだと思います。


2

上記のすべてに同意します。

例外を発生させる以外に、オブジェクトの初期化で何か問題が発生したことを知らせる他の方法はありません。

クラスの状態がそのクラスへの入力に完全に依存しているほとんどのプログラムクラスでは、ある種のValueErrorまたはTypeErrorが発生することが予想されます。

副作用のあるクラス(ネットワークやグラフィックを実行するクラスなど)は、(たとえば)ネットワークデバイスが利用できない場合やキャンバスオブジェクトに書き込めない場合に、initでエラーを発生させる可能性があります。障害の状態をできるだけ早く知りたいことがよくあるので、これは私には理にかなっているように思えます。


2

initからエラーを発生させることが避けられない場合もありますが、initで多くの作業を行うことは悪いスタイルです。ファクトリまたは疑似ファクトリ(設定されたオブジェクトを返す単純なクラスメソッド)の作成を検討する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.