6

Pythonコードで適切な入力妥当性チェックを実行しようとしていますが、簡潔にする必要もあります。つまり、私が行きたくない解決策はこれです:

def some_func(int_arg, str_arg, other_arg):
    try:
        int_arg = int(int_arg)
    except TypeError, ValueError
        logging.error("int_arg must respond to int()")
        raise TypeError
    try:
        if str_arg is not None:
            str_arg = str(str_arg) 
    except TypeError
        logging.error("Okay, I'm pretty sure this isn't possible, bad example")
        raise TypeError
    if other_arg not in (VALUE1, VALUE2, VALUE3):
        logging.error("other arg must be VALUE1, VALUE2, or VALUE3")
        raise TypeError

これはコードが多すぎて、3つの引数をチェックするだけに費やすにはスペースが多すぎます。

私の現在のアプローチはこれです:

def some_func(int_arg, str_arg, other_arg):
    try:
        int_arg = int(int_arg)  #int_arg must be an integer
        str_arg is None or str_arg = str(str_arg)  #str_arg is optional, but must be a string if provided
        assert other_arg in (VALUE1, VALUE2, VALUE3)
    catch TypeError, ValueError, AssertionError:
        logging.error("Bad arguments given to some_func")
        throw TypeError

私はログメッセージの特異性を失いますが、これは私の意見でははるかに簡潔で正直に読みやすくなっています。

特に疑問に思っているのは、assertステートメントの使用です。入力の有効性をチェックする方法としてアサーションを使用することはお勧めできないことを読みましたが、これが正当な使用方法であるかどうか疑問に思いました。
そうでない場合でも、このチェックを実行する(または一般的にこの検証を実行する)同様の方法はありますか?

4

4 に答える 4

10

引数を検証するデコレータを発明することができます。

構文は次のようになります。

@validate(0, int)
@validate(1, str, logMessage='second argument must respond to str()')
@validate(2, customValidationFunction)
def some_func(int_arg, str_arg, other_arg):
    # the control gets here only after args are validated correctly
    return int_arg * str_arg

これは、検証デコレータファクトリの単純な実装です。

def validate(narg, conv, logMessage = None):
    def decorate(func):
        def funcDecorated(*args):
            newArgs = list(args)
            try:
                newArgs[narg] = conv(newArgs[narg])
            except Exception, e:
                # wrong argument! do some logging here and re-raise
                raise Exception("Invalid argument #{}: {}".format(narg, e))
            else:
                return func(*newArgs)

        return funcDecorated
    return decorate

はい、ここには少し関数が入れ子になっていますが、それはすべて理にかなっています。説明させてください:

  • デコレータは、ある関数を受け取り、別の関数を返すものです。
  • いくつかの設定を取り、これらの設定に従って動作validate(narg, converter)する特定のデコレータ()を返す関数になりたいdecorate
  • decorate次に、 ( )と同じ引数を取り、入力関数と初期設定の観点から記述されfuncた新しい関数を作成することにより、いくつかの位置引数をとる特定の関数()を装飾するために使用されます。funcDecoratedfunc*argsfuncnargconv

実際の検証はfuncDecorated内で行われます。

  • 入力引数リストを取り、
  • n番目の引数を検証および/または変換することによって(何をするにしてconvも)置き換えます。
  • func変更された引数リストを使用して入力を呼び出します。

複数の引数に対してこれを行うためにvalidate、異なる引数を使用して複数回適用します。(一度だけ飾るように書き直すことは可能ですが、この方法ではIMOの方がより明確に見えます。)

実際の動作をご覧ください:http://ideone.com/vjgIS


どちらかを実行できることに注意してくださいconv...

  • 検証関数として(受け取ったものはすべて返しますが、無効な場合はスローします)
  • 変換関数として(変換された値を返し、変換できない場合はスローします)

この実装はキーワード引数を処理しないことに注意してください。

于 2012-08-28T07:02:12.783 に答える
1

アサーションの使用は、引数の有効性(タイプ、クラス、または値、正確にはチェックしているもの)をチェックするのにまったく問題ありません。

あなたが言った:

特に疑問に思っているのは、assertステートメントの使用です。

ですから、このページはあなたにとって役立つと思います。

特にこの部分:

アサーションは、ユーザー入力の誤りや、ファイルが見つからないなどのオペレーティングシステム/環境の障害が原因で発生する可能性のある障害ケースをテストするために使用しないでください。

于 2012-08-28T07:59:41.137 に答える
1

更新:mypyを見てください:https ://mypy.readthedocs.io/en/stable/

def some_func(int_arg: int, str_arg: str, other_arg):

@Vicentは素晴らしいページへのリンクを提供しました(多分それはその間に編集されました)。チェックが必要な場合は、次のようにタイプチェックすることをお勧めします

from types import IntType, StringType
def some_func(int_arg, str_arg, other_arg):
    assert type(int_arg) == IntType, "id is not an integer: %r" % int_arg
    assert type(str_arg) == StringType or not str_arg
    assert other_arg in (VALUE1, VALUE2, VALUE3), "other arg must be VALUE1, VALUE2, or VALUE3"

assertの良いところは、失敗したコード行が表示されることです。

于 2017-05-12T15:10:01.473 に答える
0

契約ライブラリをご覧ください。これにより、関数パラメーター(およびその戻り値)に対する制約を宣言できます。

于 2012-08-28T08:04:02.480 に答える