假设我有一些功能,f
:
def f (a=None):
print a
现在,如果我有dct = {"a":"Foo"}
字典,我可以致电f(**dct)
并打印结果Foo
。
但是,假设我有一个字典dct2 = {"a":"Foo", "b":"Bar"}
。如果我致电f(**dct2)
,我会得到一个
TypeError: f() got an unexpected keyword argument 'b'
足够公平。但是,无论如何,在f
的定义中或者在调用它时,告诉Python只是忽略任何非参数名称的键?优选允许指定默认值的方法。
答案 0 :(得分:23)
作为@Bas发布的答案的扩展,我建议将kwargs参数(变长关键字参数)作为函数的第二个参数添加
>>> def f (a=None, **kwargs):
print a
>>> dct2 = {"a":"Foo", "b":"Bar"}
>>> f(**dct2)
Foo
这一定就足够了
答案 1 :(得分:13)
这可以通过使用**kwargs
来完成,它允许您收集dict中所有未定义的关键字参数:
def f(**kwargs):
print kwargs['a']
快速测试:
In [2]: f(a=13, b=55)
13
编辑如果您仍想使用默认参数,则将原始参数保留为默认值,但只需添加**kwargs
以吸收所有其他参数:
In [3]: def f(a='default_a', **kwargs):
...: print a
...:
In [4]: f(b=44, a=12)
12
In [5]: f(c=33)
default_a
答案 2 :(得分:4)
如果您无法更改函数定义以使用未指定的** kwargs,则可以使用旧版python中的argspec函数或Python 3.6中的签名检查方法,通过关键字参数过滤传入的字典。
import inspect
def filter_dict(dict_to_filter, thing_with_kwargs):
sig = inspect.signature(thing_with_kwargs)
filter_keys = [param.name for param in sig.parameters.values() if param.kind == param.POSITIONAL_OR_KEYWORD]
filtered_dict = {filter_key:dict_to_filter[filter_key] for filter_key in filter_keys}
return filtered_dict
def myfunc(x=0):
print(x)
mydict = {'x':2, 'y':3}
filtered_dict = filter_dict(mydict, myfunc)
myfunc(**filtered_dict) # 2
myfunc(x=3) # 3
答案 3 :(得分:1)
@Aviendha的回答很好,根据她的帖子,在Python 3.6中编写了一个支持函数的关键字参数签名中默认值的增强版本
import inspect
from inspect import Parameter
import functools
from typing import Callable, Any
def ignore_unexpected_kwargs(func: Callable[..., Any]) -> Callable[..., Any]:
sig = inspect.signature(func)
params = sig.parameters.values()
def filter_kwargs(kwargs: dict) -> dict:
_params = filter(lambda p: p.kind in {Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY}, params)
res_kwargs = {
param.name: kwargs[param.name]
for param in _params if param.name in kwargs
}
return res_kwargs
def contain_var_keyword() -> bool:
return len(params) >= 1 and any(filter(lambda p: p.kind == Parameter.VAR_KEYWORD, params))
def contain_var_positional() -> bool:
return len(params) >= 1 and any(filter(lambda p: p.kind == Parameter.VAR_POSITIONAL, params))
@functools.wraps(func)
def wrapper(*args, **kwargs) -> Any:
kwargs = filter_kwargs(kwargs)
return func(*args, **kwargs)
ret_func = func
if not contain_var_keyword():
if contain_var_positional():
raise RuntimeError('*args not supported')
ret_func = wrapper
return ret_func
if __name__ == "__main__":
@ignore_unexpected_kwargs
def foo(a, b=0, c=3):
return a, b, c
assert foo(0, 0, 0) == (0, 0, 0)
dct = {'a': 1, 'b': 2}
assert foo(**dct) == (1, 2, 3)
@ignore_unexpected_kwargs
def bar(a, b, **kwargs):
return a, b, kwargs.get('c', 3)
assert bar(**{'a': 1, 'b': 2, 'c': 4}) == (1, 2, 4)
答案 4 :(得分:0)
我解决了@Menglong_Li所没有的一些问题,并简化了代码。
import inspect
import functools
def ignore_unmatched_kwargs(f):
"""Make function ignore unmatched kwargs.
If the function already has the catch all **kwargs, do nothing.
"""
if any(param.kind == inspect.Parameter.VAR_KEYWORD for param in inspect.signature(f).parameters.values()):
return f
#
@functools.wraps(f)
def inner(*args,**kwargs):
# For each keyword arguments recognised by f,
# take their binding from **kwargs received
filtered_kwargs = {
name:kwargs[name]
for name,param in inspect.signature(f).parameters.items() if (
param.kind is inspect.Parameter.KEYWORD_ONLY or
param.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
) and
name in kwargs
}
return f(*args,**filtered_kwargs)
return inner
以下是一些广泛的测试案例:
@ignore_unmatched_kwargs
def positional_or_keywords(x,y):
return x,y
@ignore_unmatched_kwargs
def keyword_with_default(x,y,z=True):
return x,y,z
@ignore_unmatched_kwargs
def variable_length(x,y,*args,**kwargs):
return x,y,args,kwargs
@ignore_unmatched_kwargs
def keyword_only(x,*,y):
return x,y
# these test should run without error
print(
positional_or_keywords(x=3,y=5,z=10),
positional_or_keywords(3,y=5),
positional_or_keywords(3,5),
positional_or_keywords(3,5,z=10),
keyword_with_default(2,2),
keyword_with_default(2,2,z=False),
keyword_with_default(2,2,False),
variable_length(2,3,5,6,z=3),
keyword_only(1,y=3),
sep='\n'
)
# these test should raise error
print(
#positional_or_keywords(3,5,6,4),
#keyword_with_default(2,2,3,z=False),
#keyword_only(1,2),
sep='\n'
)
答案 5 :(得分:0)
我使用了 Aviendha's 的想法来构建我自己的想法。它仅针对一个非常简单的案例进行了测试,但它可能对某些人有用:
import inspect
def filter_dict(func, kwarg_dict):
sign = inspect.signature(func).parameters.values()
sign = set([val.name for val in sign])
common_args = sign.intersection(kwarg_dict.keys())
filtered_dict = {key: kwarg_dict[key] for key in common_args}
return filtered_dict
在此特定情况下进行了测试:
def my_sum(first, second, opt=3):
return first + second - opt
a = {'first': 1, 'second': 2, 'third': 3}
new_kwargs = filter_dict(my_sum, a)
该示例返回 new_args = {'first': 1, 'second': 2}
,然后可以将其作为 my_sum
传递给 my_sum(**new_args)