您可以列出函数接收的关键字参数吗?


104

我有一个字典,我需要将键/值作为关键字参数传递..例如。

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,但是我想知道:是否有一种方法可以以编程方式列出特定函数所采用的关键字参数?

Answers:


150

比起直接检查代码对象并计算出变量,更好的方法是使用检查模块。

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

1
假设使用代码对象的方法或多或少都是相同的,那么必须再导入一个模块有好处吗?
jmetz

@jmets-绝对-使用库模块实际上总比滚动自己的库更好。另外,代码对象上的属性更内部,可以更改(例如,请注意,该属性已移至pyhon3中的代码)。如果将这些内部组件中的某些更改,则将模块用作接口可为您提供更多的前瞻性。它还会做您可能没有想到的事情,例如在无法检查的函数(例如C函数)上抛出适当的类型错误。
2014年

13
inspect.getargspec(f)从Python 3.0开始不推荐使用;现代方法是inspect.signature(f)
gerrit

仅供参考,如果您想支持Cython和Python,则此方法不适用于Cython'd函数。co_varnames另一方面,该选项在两种情况下均有效。
2015年

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)

现在,您可以使用已知的args调用函数,但是要提取kwargs,例如:

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

假定签名中func不使用no *args**kwargsmagic。


如果我只想打印出“关键字”自变量“关键字”怎么办?

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 positional和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中添加一个子句,以通过检查是否p.default等于来检查是否存在默认值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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.