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


104

私はdictを持っています。これをキーワード引数としてキー/値を渡す必要があります。たとえば。

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

これは、罰金に動作します、しかしによって受け付けられませんd_argsの辞書内の値がある場合exampleの例機能は以下のように定義されている場合、関数は、それは明らかに死ぬ...セイdef example(kw2):

の生成d_argsexample関数も制御しないため、これは問題です。どちらも外部モジュールexampleからのものであり、dictからのキーワード引数の一部しか受け入れません。

理想的には

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

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

回答:


150

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

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

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

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の無効な値のランタイムチェックは明らかに検出できません。)


いいね!この機能を知らなかった!
DzinX 2008年

1
コードオブジェクトを使用するメソッドが多かれ少なかれ同一であることを考えると、もう1つのモジュールをインポートする必要があったことには利点がありますか?
jmetz 14

@jmets-間違いなく-あなた自身のものをロールするよりもライブラリモジュールを使用する方が事実上常に優れています。また、コードオブジェクトの属性はより内部的であり、変更の余地があります(たとえば、これはpyhon3のコードに移動したことに注意してください)。モジュールをインターフェースとして使用することで、これらの内部の一部が変更された場合に備えて、もう少し保証します。また、検査できない関数(C関数など)に対して適切な型エラーをスローするなど、考えていなかったかもしれません。
ブライアン

13
inspect.getargspec(f)Python 3.0以降は非推奨です。現代の方法はinspect.signature(f)です。
gerrit 2017年

参考までに、CythonとPythonをサポートする場合、このメソッドはCythonの関数では機能しません。co_varnamesオプションは、他の一方で、両方で作業を行います。
2017年

32

これにより、すべての渡せる引数の名前、キーワード引数と非キーワード引数が出力されます。

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

編集:小さな追加:関数のキーワード引数のみ本当に必要な場合は、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


「キーワード」引数「キー」のみを出力したい場合はどうすればよいですか?
Jia

7

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={})

7

Python 3ソリューションでは、知りたいパラメーターの種類にinspect.signature応じてフィルターを使用できます。

位置またはキーワード、キーワードのみ、var位置および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_POSITIONALvarキーワードを使用し、でvarキーワードを使用しVAR_KEYWORDます。

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


3

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.