私は簡単なコマンドラインユーティリティを作成し、適切な関数にリンクするキーワードを含む一種のcaseステートメントとして辞書を使用していました。関数にはすべて異なる量の引数が必要なので、現在、ユーザーが各関数に必要な正しい量の引数を入力したかどうかを確認するために、必要な量を{Keyword:(FunctionName, AmountofArguments)}
の形式で辞書のcaseステートメント内に配置しました。
この現在の設定は完全に正常に機能しますが、関数に必要な引数の数を決定する方法があり、グーグルの試みがこれまでのところ何の価値も返さないかどうかを自己改善するために疑問に思っていましたが、argsとkwargsがどのように見えるかがわかります許可される引数の数に制限がないため、このようなコマンドを台無しにする可能性があります。
関数の引数の名前とデフォルト値を取得します。 4つのタプルが返されます:(args、varargs、varkw、defaults)。 argsは、引数名のリストです(ネストされたリストが含まれる場合があります)。 varargsおよびvarkwは、*および**引数またはNoneの名前です。 defaultsは、デフォルトの引数値のタプルです。デフォルトの引数がない場合は、Noneです。このタプルにn個の要素がある場合、それらはargsにリストされている最後のn個の要素に対応します。
可変引数と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
Python 3では、someMethod.__code__.co_argcount
を使用します
(someMethod.func_code.co_argcount
が機能しなくなったため)
これはすでに回答済みですが、検査モジュールがなくてもsomeMethod.func_code.co_argcount
を使用できます。
各コマンドを、コマンドの一般的な構造を定義する抽象ベースから派生したクラスにします。可能な限り、コマンドプロパティの定義は、そのデータを処理する基本クラスで定義されたメソッドを使用してクラス変数に配置する必要があります。
これらの各サブクラスをファクトリクラスに登録します。このファクトリクラスは引数リストを取得し、適切なコマンドサブクラスをインスタンス化することによって実行するコマンドを決定します。
引数のチェックは、コマンド基本クラスから適切に定義された一般的なメソッドを使用して、コマンドサブクラス自体によって処理されます。
このように、同じものを繰り返しコーディングする必要はなく、switchステートメントをエミュレートする必要もありません。また、新しいクラスを追加して登録するだけなので、コマンドの拡張と追加が非常に簡単になります。他に変更するものはありません。
すばらしい質問です。コールバック引数を取る関数を書きたいという問題がありました。そのコールバックの引数の数に応じて、別の方法で呼び出す必要があります。
私は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コード化された関数の実装を検査できないという事実に遭遇する人々を助けるかもしれません。たぶん人々はより良い提案を持っていますか?