简体   繁体   English

python检查函数是否接受**kwargs

[英]python check if function accepts **kwargs

is there a way to check if a function accepts **kwargs before calling it eg有没有办法在调用一个函数之前检查它是否接受 **kwargs 例如

def FuncA(**kwargs):
    print 'ok'

def FuncB(id = None):
    print 'ok'

def FuncC():
    print 'ok'

args = {'id': '1'}

FuncA(**args)
FuncB(**args)
FuncC(**args)

When I run this FuncA and FuncB would be okay but FuncC errors with got an unexpected keyword argument 'id' as it doesn't accept any arguments当我运行这个 FuncA 和 FuncB 时会好的,但是 FuncC 错误, got an unexpected keyword argument 'id'因为它不接受任何参数

try:
    f(**kwargs)
except TypeError:
    #do stuff

It's easier to ask forgiveness than permission .请求原谅比允许更容易

def foo(a, b, **kwargs):
  pass

import inspect
args, varargs, varkw, defaults = inspect.getargspec(foo)
assert(varkw=='kwargs')

This only works for Python functions.这仅适用于 Python 函数。 Functions defined in C extensions (and built-ins) may be tricky and sometimes interpret their arguments in quite creative ways.在 C 扩展(和内置)中定义的函数可能很棘手,有时会以非常有创意的方式解释它们的参数。 There's no way to reliably detect which arguments such functions expect.没有办法可靠地检测这些函数期望的参数。 Refer to function's docstring and other human-readable documentation.请参阅函数的文档字符串和其他人类可读的文档。

func is the function in question. func是有问题的函数。

with python2, it's:使用python2,它是:

inspect.getargspec(func).keywords is not None

python3 is a bit tricker, following https://www.python.org/dev/peps/pep-0362/ the kind of parameter must be VAR_KEYWORD python3有点狡猾,跟随https://www.python.org/dev/peps/pep-0362/参数的kind必须是VAR_KEYWORD

Parameter.VAR_KEYWORD - a dict of keyword arguments that aren't bound to any other parameter. Parameter.VAR_KEYWORD - 未绑定到任何其他参数的关键字参数的字典。 This corresponds to a "**kwargs" parameter in a Python function definition.这对应于 Python 函数定义中的“**kwargs”参数。

any(param for param in inspect.signature(func).parameters.values() if param.kind == param.VAR_KEYWORD)

Seeing that there are a multitude of different answers in this thread, I thought I would give my two cents, using inspect.signature() .看到这个帖子中有很多不同的答案,我想我会使用inspect.signature()给我两美分。

Suppose you have this method:假设你有这个方法:

def foo(**kwargs):

You can test if **kwargs are in this method's signature:您可以测试**kwargs是否在此方法的签名中:

import inspect

sig = inspect.signature(foo)
params = sig.parameters.values()
has_kwargs = any([True for p in params if p.kind == p.VAR_KEYWORD])

More更多的

Getting the parameters in which a method takes is also possible:获取方法所采用的参数也是可能的:

import inspect

sig = inspect.signature(foo)
params = sig.parameters.values()
for param in params:
    print(param.kind)

You can also store them in a variable like so:您还可以将它们存储在一个变量中,如下所示:

kinds = [param.kind for param in params]

# [<_ParameterKind.VAR_KEYWORD: 4>]

Other than just keyword arguments, there are 5 parameter kinds in total, which are as follows:除了关键字参数,一共有5种参数类型,如下:

POSITIONAL_ONLY        # parameters must be positional

POSITIONAL_OR_KEYWORD  # parameters can be positional or keyworded (default)

VAR_POSITIONAL         # *args

KEYWORD_ONLY           # parameters must be keyworded 

VAR_KEYWORD            # **kwargs

Descriptions in the official documentation can be found here .官方文档中的描述可以在这里找到。

Examples例子

POSITIONAL_ONLY

def foo(a, /): 
# the '/' enforces that all preceding parameters must be positional

foo(1) # valid
foo(a=1) #invalid

POSITIONAL_OR_KEYWORD

def foo(a):
# 'a' can be passed via position or keyword
# this is the default and most common parameter kind

VAR_POSITIONAL

def foo(*args):

KEYWORD_ONLY

def foo(*, a):
# the '*' enforces that all following parameters must by keyworded

foo(a=1) # valid
foo(1) # invalid

VAR_KEYWORD

def foo(**kwargs):

For python > 3 you should to use inspect.getfullargspec .对于 python > 3,你应该使用inspect.getfullargspec

import inspect

def foo(**bar):
    pass

arg_spec = inspect.getfullargspec(foo)
assert arg_spec.varkw and arg_spec.varkw == 'bar'

According to https://docs.python.org/2/reference/datamodel.html you should be able to test for use of **kwargs using co_flags :根据https://docs.python.org/2/reference/datamodel.html,您应该能够使用co_flags测试**kwargs使用:

>>> def blah(a, b, kwargs):
...     pass

>>> def blah2(a, b, **kwargs):
...     pass

>>> (blah.func_code.co_flags & 0x08) != 0
False
>>> (blah2.func_code.co_flags & 0x08) != 0
True

Though, as noted in the reference this may change in the future, so I would definitely advise to be extra careful.不过,正如参考资料中所指出的,这在未来可能会发生变化,所以我绝对建议要格外小心。 Definitely add some unit tests to check this feature is still in place.一定要添加一些单元测试来检查此功能是否仍然存在。

It appears that you want to check whether the function receives an 'id' keyword argument.您似乎想检查该函数是否收到“id”关键字参数。 You can't really do that by inspection because the function might not be a normal function, or you might have a situation like that:您无法通过检查真正做到这一点,因为该功能可能不是正常功能,或者您可能会遇到这样的情况:

def f(*args, **kwargs):
    return h(*args, **kwargs)

g = lambda *a, **kw: h(*a, **kw)

def h(arg1=0, arg2=2):
    pass

f(id=3) still fails f(id=3)仍然失败

Catching TypeError as suggested is the best way to do that, but you can't really figure out what caused the TypeError .按照建议捕获TypeError是最好的方法,但您无法真正弄清楚是什么导致了TypeError For example, this would still raise a TypeError :例如,这仍然会引发TypeError

def f(id=None):
     return "%d" % id

f(**{'id': '5'})

And that might be an error that you want to debug.这可能是您想要调试的错误。 And if you're doing the check to avoid some side effects of the function, they might still be present if you catch it.如果您正在进行检查以避免该函数的某些副作用,如果您发现它,它们可能仍然存在。 For example:例如:

class A(object):
   def __init__(self): self._items = set([1,2,3])
   def f(self, id): return self._items.pop() + id

a = A()
a.f(**{'id': '5'})

My suggestion is to try to identify the functions by another mechanism.我的建议是尝试通过另一种机制来识别功能。 For example, pass objects with methods instead of functions, and call only the objects that have a specific method.例如,通过方法而不是函数传递对象,并且只调用具有特定方法的对象。 Or add a flag to the object or the function itself.或者向对象或函数本身添加一个标志。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM