web-dev-qa-db-ja.com

関数が受け取るキーワード引数をリストできますか?

キーワード引数としてキー/値を渡す必要がある辞書があります。たとえば、..

_d_args = {'kw1': 'value1', 'kw2': 'value2'}
example(**d_args)
_

これは正常に機能しますが、butd_args dictにexample関数で受け入れられない値がある場合、明らかに死にます。例の関数がdef example(kw2):として定義されている場合

_d_args_またはexample関数の生成を制御しないため、これは問題です。これらは両方とも外部モジュールからのものであり、exampleは一部のみを受け入れます辞書からのキーワード引数..

理想的にはただやる

_parsed_kwargs = feedparser.parse(the_url)
valid_kwargs = get_valid_kwargs(parsed_kwargs, valid_for = PyRSS2Gen.RSS2)
PyRSS2Gen.RSS2(**valid_kwargs)
_

私はおそらく、有効なキーワード引数のリストから辞書を単にフィルタリングしますが、私は疑問に思っていました:特定の関数が取るキーワード引数をプログラムでリストする方法はありますか?

98
dbr

コードオブジェクトを直接検査して変数を処理するよりも少し良いのは、検査モジュールを使用することです。

>>> import inspect
>>> def func(a,b,c=42, *args, **kwargs): pass
>>> inspect.getargspec(func)
(['a', 'b', 'c'], 'args', 'kwargs', (42,))

特定の引数のセットで呼び出し可能かどうかを知りたい場合は、デフォルトが指定されていない引数が必要です。これらは次の方法で取得できます。

def getRequiredArgs(func):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if defaults:
        args = args[:-len(defaults)]
    return args   # *args and **kwargs are not required, so ignore them.

次に、特定の辞書から何が欠けているかを伝える関数は次のとおりです。

def missingArgs(func, argdict):
    return set(getRequiredArgs(func)).difference(argdict)

同様に、無効な引数を確認するには、次を使用します。

def invalidArgs(func, argdict):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if varkw: return set()  # All accepted
    return set(argdict) - set(args)

したがって、呼び出し可能かどうかの完全なテストは次のとおりです。

def isCallableWithArgs(func, argdict):
    return not missingArgs(func, argdict) and not invalidArgs(func, argdict)

(これは、Pythonのarg解析に関してのみ有効です。kwargsの無効な値のランタイムチェックは明らかに検出できません。)

139
Brian

これにより、すべての受け渡し可能な引数、キーワード、およびキーワード以外の引数の名前が出力されます。

def func(one, two="value"):
    y = one, two
    return y
print func.func_code.co_varnames[:func.func_code.co_argcount]

これは、最初のco_varnamesが常にパラメーターであるためです(次は上記の例のyのようなローカル変数です)。

だから今、あなたは関数を持つことができます:

def getValidArgs(func, argsDict):
    '''Return dictionary without invalid function arguments.'''
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validArgs)

次のように使用できます:

>>> func(**getValidArgs(func, args))

[〜#〜] edit [〜#〜]:小さな追加:キーワード引数のみが必要な場合関数の場合、func_defaultsを使用できます。それらを抽出する属性:

def getValidKwargs(func, argsDict):
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    kwargsLen = len(func.func_defaults) # number of keyword arguments
    validKwargs = validArgs[-kwargsLen:] # because kwargs are last
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validKwargs)

既知の引数を使用して関数を呼び出すことができますが、抽出されたkwargs、たとえば:

func(param1, param2, **getValidKwargs(func, kwargsDict))

これは、funcがその署名で*argsまたは**kwargsマジックを使用しないことを前提としています。

30
DzinX

In Python 3.0:

>>> import inspect
>>> import fileinput
>>> print(inspect.getfullargspec(fileinput.input))
FullArgSpec(args=['files', 'inplace', 'backup', 'bufsize', 'mode', 'openhook'],
varargs=None, varkw=None, defaults=(None, 0, '', 0, 'r', None), kwonlyargs=[], 
kwdefaults=None, annotations={})
6
jfs

Python 3ソリューションの場合、 inspect.signature を使用し、 パラメーターの種類 に従ってフィルターすることができますについて知る。

位置またはキーワード、キーワードのみ、var positionおよびvarキーワードパラメータを使用したサンプル関数の取得:

def spam(a, b=1, *args, c=2, **kwargs):
    print(a, b, args, c, kwargs)

署名オブジェクトを作成できます:

from inspect import signature
sig =  signature(spam)

次に、リスト内包表記でフィルタリングして、必要な詳細を見つけます。

>>> # positional or keyword
>>> [p.name for p in sig.parameters.values() if p.kind == p.POSITIONAL_OR_KEYWORD]
['a', 'b']
>>> # keyword only
>>> [p.name for p in sig.parameters.values() if p.kind == p.KEYWORD_ONLY]
['c']

また、同様に、p.VAR_POSITIONALを使用したvar位置とVAR_KEYWORDを使用したvarキーワード。

さらに、ifに句を追加して、p.defaultp.emptyと等しいかどうかをチェックすることにより、デフォルト値が存在するかどうかを確認できます。

DzinXの答えを拡張する:

argnames = example.func_code.co_varnames[:func.func_code.co_argcount]
args = dict((key, val) for key,val in d_args.iteritems() if key in argnames)
example(**args)
3
Claudiu