Pythonの不正な引数と不正な引数の組み合わせについて、どの例外を発生させる必要がありますか?


544

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-誰もがBadValueErrorPythonで独自のを作成することはよく理解されていますか、それとも別の好ましい方法がありますか?

回答:


608

より具体的な例外が必要でない限り、私は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と同じように使用されます。


65
>「それで、なぜそれを使わないのですか?」-特異性。おそらく、「MyValueError」のような外層でキャッチしたいのですが、「ValueError」のすべてではありません。
ケビンリトル

7
ええ、特異性の問題の一部は他にValueErrorが発生する場所です。呼び出し先の関数が引数を好み、内部でmath.sqrt(-1)を呼び出す場合、呼び出し元はValueErrorをキャッチしている可能性があり、その引数は不適切であると想定してます。この場合のメッセージを確認するだけかもしれません...
cdleary 2009年

3
その議論が成立するかどうかはわかりません。誰かがを呼び出している場合math.sqrt(-1)、それはとにかく修正する必要があるプログラミングエラーです。ValueErrorは、通常のプログラム実行に巻き込まれることを意図していないか、またはから派生しRuntimeErrorます。
ereOn、2015年

2
エラーが引数のNUMBERにある場合、引数の数が可変の関数の場合...たとえば、引数が偶数の引数でなければならない関数の場合、一貫性を保つためにTypeErrorを発生させる必要があります。また、a)ユースケースがある場合、またはb)他のユーザーが使用するライブラリをエクスポートする場合を除いて、独自のクラスを作成しないでください。時期尚早な機能はコードの死です。
Erik Aronesty 16

104

から継承する ValueError

class IllegalArgumentError(ValueError):
    pass

独自の例外を作成した方がよい場合もありますが、組み込みの例外を継承するので、可能な限り近いものにできます。

その特定のエラーをキャッチする必要がある場合は、名前を付けると役立ちます。


26
クラスとカスタム例外の作成を停止する-pyvideo.org/video/880/stop-writing-classes
Hamish Grubijan '15

40
@HamishGrubijanそのビデオはひどいです。だれかがクラスの適切な使用を提案したとき、彼は「クラスを使用しないでください」という口実だけを非難しました。鮮やかさ。クラスは良いです。しかし、それを私の言葉にしないでください
ロブ・グラント

12
@RobertGrantいいえ、取得できません。そのビデオは、文字通り「クラスを使用しない」ことについての本ではありません。複雑すぎないことです。
RayLuo

15
@RayLuoあなたはビデオが何を言っているかを健全性チェックし、それを口当たりの良い、賢明な代替メッセージに変換したかもしれませんが、それはビデオが言っていることであり、それは多くの経験と常識を持っていない誰かが去ることですと。
Rob Grant

3
@SamuelSantana私が言ったように、誰かが手を挙げて「Xはどうだ?」Xは良いアイデアだったのに、「別のクラスを作ってはいけない」と彼は言った。かなりクリアな。鍵はバランスです。問題は、それが実際に生活するには曖昧すぎることです:-)
Rob Grant

18

これを処理する最良の方法は、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例外の間違った引数」のグーグル検索でこのページを見つけただけで、この質問がされて以来、明白な(私にとって)答えが10年間提案されなかったことに驚いています。


8
何も私を驚かせませんが、関数に渡された引数の一部で型が間違っている場合、TypeErrorが正しい例外であることに100%同意します。ValueErrorは、変数のタイプが正しいが、その内容と値が意味をなさない場合に適しています。
user3504575

これはおそらく引数が欠落しているか、呼び出されていないためだと思いますが、質問は正しく与えられた引数に関するものですが、与えられた引数のを含むより高い抽象化レベルでは正しくありません。しかし、実際には前者を探していたので、とにかく賛成票を持ってください。
誰も

2
@ user3504575と@Nobodyが言ったように、引数が関数のシグネチャと一致しない場合はTypeErrorが使用されます(位置引数の数が正しくない、キーワード引数の名前が間違っている、引数のタイプが間違っている)。ただし、関数呼び出し時にValueErrorが使用されます。署名に一致しますが、引数値が無効です(たとえば、を呼び出すint('a'))。ソース
goodmami

OPの質問は「無効な引数の組み合わせ」に言及しているので、TypeErrorは、渡された引数に対して関数のシグネチャが本質的に間違っている場合であるため、適切であると思われます。
Jボーンズ

あなたの例はsum()、引数なしで呼び出します。これはですTypeErrorが、OPは、引数の型が正しい場合、引数値の「不正な」組み合わせに関係していました。この場合、saverecurseはどちらもブール値ですが、そうである場合recurseTrueにしsaveないでくださいFalse。これはValueErrorです。質問のタイトルの一部の解釈にはが回答することに同意しますがTypeError、提示されている例ではそうではありません。
goodmami


8

それは議論の問題が何であるかによる。

引数のタイプが間違っている場合は、TypeErrorを送出します。たとえば、これらのブール値の1つではなく文字列を取得した場合です。

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

または、この特定のケースでは、recurseの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

これが最も正確な答えだと思います。これは明らかに過小評価されています(私のものを含め、これまでのところ7票)。
Siu Ching Pong -Asuka Kenji-

-1

私は確かに私はからの継承に同意していないよValueErrorそれがされているマニュアルの私の解釈- ValueErrorされるだけで、それを継承したり、自分が間違っているようだ、それを上げる...組み込みコマンドによって提起されることになって。

組み込みの操作または関数が、型は正しいが値が不適切な引数を受け取ったときに発生します。状況は、IndexErrorなどのより正確な例外では説明されません。

- とValueErrorのドキュメント


google.com/codesearch?q=lang:python+class \ + \ w Error(([^ E] \ w * | E [^ x] \ w)):とgoogle.com/codesearch?q=langを比較: python + class \ + \ w * Error(Exception):
Markus Jarderot

13
その言い訳は、組み込みがそれを上げることを単に意味し、組み込みのみがそれを上げることができるというわけではありません。この例では、Pythonライブラリーが外部ライブラリーが提起するものについて話すのは完全に適切ではありません。
イグナシオバスケスエイブラムス

5
私がこれまでに見たすべてのPythonソフトウェアValueErrorがこの種のものに使用されているため、ドキュメントを読みすぎていると思います。
James Bennett、

6
エラー、Google Code検索を使用してこれを主張する場合:google.com/codesearch ?q=lang%3Apython+raise%5C+ValueError#Zope、xen、Django、Mozilla(および結果の最初のページからです)。組み込みの例外が当てはまる場合は、それを使用します。
DBR

7
述べたように、ドキュメントがあいまいです。「組み込み操作または組み込み関数が受信したときに発生」または「関数または組み込み操作が受信したときに発生」のいずれかとして記述されているはずです。もちろん、元の意図が何であれ、現在の慣行はそれを打ち負かしています(@dbrが指摘するように)。したがって、2番目のバリアントとして書き直す必要があります。
代名詞

-1

独自の例外をロールするというMarkusの提案に同意しますが、例外のテキストは、問題が個々の引数値ではなく引数リストにあることを明確にする必要があります。私は提案します:

class BadCallError(ValueError):
    pass

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

これはPythonの標準の例外ではないのですか?

一般的に、関数への悪い入力(呼び出し側の障害)を関数内の悪い結果(私の障害)から区別する際に、Pythonスタイルを少しシャープにしたいと思います。したがって、引数の値エラーとローカルの値エラーを区別するためのBadArgumentErrorも存在する可能性があります。


KeyErrorキーワードが見つからない場合はレイズします(明示的なキーワード**kwargsが見つからないのは、そのキーがないdict と意味的に同じであるためです)。
カウベルト2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.