web-dev-qa-db-ja.com

関数に必要なパラメーターの量をプログラムで決定する-Python

私は簡単なコマンドラインユーティリティを作成し、適切な関数にリンクするキーワードを含む一種のcaseステートメントとして辞書を使用していました。関数にはすべて異なる量の引数が必要なので、現在、ユーザーが各関数に必要な正しい量の引数を入力したかどうかを確認するために、必要な量を{Keyword:(FunctionName, AmountofArguments)}の形式で辞書のcaseステートメント内に配置しました。

この現在の設定は完全に正常に機能しますが、関数に必要な引数の数を決定する方法があり、グーグルの試みがこれまでのところ何の価値も返さないかどうかを自己改善するために疑問に思っていましたが、argsとkwargsがどのように見えるかがわかります許可される引数の数に制限がないため、このようなコマンドを台無しにする可能性があります。

31
tomeedee

inspect.getargspec()

関数の引数の名前とデフォルト値を取得します。 4つのタプルが返されます:(args、varargs、varkw、defaults)。 argsは、引数名のリストです(ネストされたリストが含まれる場合があります)。 varargsおよびvarkwは、*および**引数またはNoneの名前です。 defaultsは、デフォルトの引数値のタプルです。デフォルトの引数がない場合は、Noneです。このタプルにn個の要素がある場合、それらはargsにリストされている最後のn個の要素に対応します。

40
gimel

可変引数とkwargsを使用しているため、通常、必要なことは不可能ですが、inspect.getargspec(Python 2.x)およびinspect.getfullargspec(Python 3.x)が近づいています。

  • Python 2.x:

    >>> import inspect
    >>> def add(a, b=0):
    ...     return a + b
    ...
    >>> inspect.getargspec(add)
    (['a', 'b'], None, None, (0,))
    >>> len(inspect.getargspec(add)[0])
    2
    
  • Python 3.x:

    >>> import inspect
    >>> def add(a, b=0):
    ...     return a + b
    ...
    >>> inspect.getfullargspec(add)
    FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={})
    >>> len(inspect.getfullargspec(add).args)
    2
    
15
Stephan202

Python 3では、someMethod.__code__.co_argcountを使用します

someMethod.func_code.co_argcountが機能しなくなったため)

4
Bobort

これはすでに回答済みですが、検査モジュールがなくてもsomeMethod.func_code.co_argcountを使用できます。

2
Josep Valls

各コマンドを、コマンドの一般的な構造を定義する抽象ベースから派生したクラスにします。可能な限り、コマンドプロパティの定義は、そのデータを処理する基本クラスで定義されたメソッドを使用してクラス変数に配置する必要があります。

これらの各サブクラスをファクトリクラスに登録します。このファクトリクラスは引数リストを取得し、適切なコマンドサブクラスをインスタンス化することによって実行するコマンドを決定します。

引数のチェックは、コマンド基本クラスから適切に定義された一般的なメソッドを使用して、コマンドサブクラス自体によって処理されます。

このように、同じものを繰り返しコーディングする必要はなく、switchステートメントをエミュレートする必要もありません。また、新しいクラスを追加して登録するだけなので、コマンドの拡張と追加が非常に簡単になります。他に変更するものはありません。

1
Ber

すばらしい質問です。コールバック引数を取る関数を書きたいという問題がありました。そのコールバックの引数の数に応じて、別の方法で呼び出す必要があります。

私はgimelの答えから始め、次にinspectモジュール(raise TypeError)とうまく反応しないビルトインを処理できるように拡張しました。

したがって、関数が正確に1つの引数を期待しているかどうかを確認するコードは次のとおりです。

def func_has_one_arg_only(func, typical_argument=None, ignore_varargs=False):
    """True if given func expects only one argument

    Example (testbench):
    assert not func_has_one_arg_only(dict.__getitem__), 'builtin 2 args'
    assert func_has_one_arg_only(lambda k: k), 'lambda 1 arg'
    assert not func_has_one_arg_only(lambda k,x: k), 'lambda 2 args'
    assert not func_has_one_arg_only(lambda *a: k), 'lambda *a'
    assert not func_has_one_arg_only(lambda **a: k), 'lambda **a'
    assert not func_has_one_arg_only(lambda k,**a: k), 'lambda k,**a'
    assert not func_has_one_arg_only(lambda k,*a: k), 'lambda k,*a'

    assert func_has_one_arg_only(lambda k: k, ignore_varargs=True), 'lambda 1 arg'
    assert not func_has_one_arg_only(lambda k,x: k, ignore_varargs=True), 'lambda 2 args'
    assert not func_has_one_arg_only(lambda *a: k, ignore_varargs=True), 'lambda *a'
    assert not func_has_one_arg_only(lambda **a: k, ignore_varargs=True), 'lambda **a'
    assert func_has_one_arg_only(lambda k,**a: k, ignore_varargs=True), 'lambda k,**a'
    assert func_has_one_arg_only(lambda k,*a: k, ignore_varargs=True), 'lambda k,*a'
    """

    try:
        import inspect
        argspec = inspect.getargspec(func)
    except TypeError:                   # built-in c-code (e.g. dict.__getitem__)
        try:
            func(typical_argument)
        except TypeError:
            return False
        else:
            return True
    else:
        if not ignore_varargs:
            if argspec.varargs or argspec.keywords:
                return False
        if 1 == len(argspec.args):
            return True
        return False
    raise RuntimeError('This line should not be reached')

*argsパラメーターを使用して、可変引数引数**kwargsおよびignore_varargsに関連する動作を制御できます。

typical_argumentパラメータは応急修理です:inspectが機能しない場合、例:前述の組み込み関数では、1つの引数を使用して関数を呼び出し、何が起こるかを確認します。

このアプローチの問題は、raise TypeErrorに複数の理由があることです。間違った数の引数が使用されているか、間違ったタイプの引数が使用されています。ユーザーがtypical_argumentを提供できるようにすることで、この問題を回避しようとしています。

これはいいことではありません。しかし、同じ質問をし、inspectがCコード化された関数の実装を検査できないという事実に遭遇する人々を助けるかもしれません。たぶん人々はより良​​い提案を持っていますか?

0
cfi