web-dev-qa-db-ja.com

Pythonでの悪い/違法な引数の組み合わせに対して、どの例外を発生させるべきですか?

Pythonで無効な引数の組み合わせを示すためのベストプラクティスについて疑問に思いました。私はあなたがそのような機能を持っているいくつかの状況に出くわしました:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

これに対する唯一の煩わしさは、すべてのパッケージがそれ自身のものを持っているということです、通常はBadValueErrorと少し異なります。 JavaにはJava.lang.IllegalArgumentExceptionがあることを私は知っています - 誰もが自分自身でBadValueErrorsをPythonで作成するのでしょうか、それとも別の好ましい方法がありますか?

449
cdleary

もっと具体的な例外が必要な場合を除き、 ValueError を送出します。

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

class BadValueError(ValueError):passを実行する意味が全くありません - あなたのカスタムクラスは ValueError と同じ意味で使われています。

496
dbr

ValueErrorから継承する

class IllegalArgumentError(ValueError):
    pass

独自の例外を作成したほうがよい場合もありますが、組み込みの例外から継承することをお勧めします。これは、できるだけ必要なものに近いものです。

その特定のエラーを捉える必要がある場合は、名前を付けると便利です。

88
Markus Jarderot

私はこれを処理するための最良の方法はPython自身がそれを処理する方法であると思います。 PythonはTypeErrorを送出します。例えば:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

私達の後輩の開発者がちょうど "python exception wrong arguments"のためのグーグル検索でこのページを見つけた、そして私はこの質問がされて以来10年以内に明白な(私に)答えが提案されなかったことに驚きます。

10
J Bones

私はほとんどの場合、この状況で使用されている組み込みのValueErrorを見たばかりです。

9
Eli Courtwright

引数の問題が何であるかによります。

引数の型が間違っている場合、TypeErrorが発生します。たとえば、これらのブール値の代わりに文字列を取得する場合。

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

ただし、Pythonでは、このようなチェックを行うことはほとんどありません。引数が実際に無効である場合、より深い関数がおそらく不平を言うでしょう。そして、ブール値のみをチェックする場合、おそらく一部のコードユーザーは、空でない文字列が常にTrueであることを知って、後で文字列を渡すだけでしょう。それは彼にキャストを保存する可能性があります。

引数に無効な値がある場合、ValueErrorを発生させます。これはあなたの場合により適しているようです:

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

または、この特定のケースでは、再帰のTrue値が保存のTrue値を意味します。私はこれをエラーからの回復と考えているので、ログで苦情を申し立てることもできます。

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True
1
Jacco van Dorp

私はValueErrorからの継承に同意するかどうかわからない - 私のドキュメントの解釈はValueErrorのみであるとされているというものです...それを継承したり自分で上げたりするのは正しくないようです。

組み込みの操作または関数が、正しい型ではあるが不適切な値を持つ引数を受け取り、IndexErrorのようなより正確な例外で状況が説明されない場合に発生します。

- ValueErrorドキュメント

1
cdleary

あなた自身の例外をロールバックするというMarkusの提案に同意してください、しかし例外のテキストは問題が個々の引数値ではなく引数リストにあることを明確にするべきです。私は提案します:

class BadCallError(ValueError):
    pass

特定の呼び出しに必要なキーワード引数が欠落している場合、または引数値が個別に有効だが互いに矛盾している場合に使用されます。 ValueErrorは、特定の引数が正しい型だが範囲外の場合でも正しい値になります。

これはPythonの標準的な例外ではないでしょうか。

一般的に、関数への悪い入力(呼び出し側の誤り)と関数内の悪い結果(私の誤り)を区別する際には、Pythonスタイルをもう少しシャープにしたいと思います。そのため、引数の値エラーとローカルの値エラーを区別するためのBadArgumentErrorもあります。

0
BobHy