我正在寻找将函数与字典结合起来的最佳方法,该字典包含的项目多于函数的输入
在这种情况下,基本** kwarg解包失败:
def foo(a,b):
return a + b
d = {'a':1,
'b':2,
'c':3}
foo(**d)
--> TypeError: foo() got an unexpected keyword argument 'c'
经过一番研究后,我想出了以下方法:
import inspect
# utilities
def get_input_names(function):
'''get arguments names from function'''
return inspect.getargspec(function)[0]
def filter_dict(dict_,keys):
return {k:dict_[k] for k in keys}
def combine(function,dict_):
'''combine a function with a dictionary that may contain more items than the function's inputs '''
filtered_dict = filter_dict(dict_,get_input_names(function))
return function(**filtered_dict)
# examples
def foo(a,b):
return a + b
d = {'a':1,
'b':2,
'c':3}
print combine(foo,d)
--> 3
我的问题是:这是一个处理这个问题的好方法,还是有更好的实践,或者是否有一种我可能缺少的语言机制?
答案 0 :(得分:23)
如何制作decorator 仅过滤允许的关键字参数:
import inspect
def get_input_names(function):
'''get arguments names from function'''
return inspect.getargspec(function)[0]
def filter_dict(dict_,keys):
return {k:dict_[k] for k in keys}
def filter_kwargs(func):
def func_wrapper(**kwargs):
return func(**filter_dict(kwargs, get_input_names(func)))
return func_wrapper
@filter_kwargs
def foo(a,b):
return a + b
d = {'a':1,
'b':2,
'c':3}
print(foo(**d))
这个装饰器的好处是它是通用的和可重用的。而且您不需要更改调用和使用目标函数的方式。
答案 1 :(得分:14)
所有这些答案都是错误的。
不可能按照你的要求去做,因为函数可能会这样声明:
UGA
现在,为什么有人会写这个呢?
因为他们不提前知道所有的论点。这是一个更现实的案例:
def foo(**kwargs):
a = kwargs.pop('a')
b = kwargs.pop('b')
if kwargs:
raise TypeError('Unexpected arguments: %r' % kwargs)
here是一些实际执行此操作的真实代码。
您可能会问为什么我们需要最后一行。为什么要将参数传递给不带任何参数的超类? Cooperative multiple inheritance。如果我的类得到一个它无法识别的参数,它不应该吞下那个参数,也不应该错误。它应该将参数传递给链,以便我可能不知道的另一个类可以处理它。如果没有人处理它,那么def __init__(self, **kwargs):
for name in self.known_arguments():
value = kwargs.pop(name, default)
self.do_something(name, value)
super().__init__(**kwargs) # The superclass does not take any arguments
将提供适当的错误消息。不幸的是,其他答案不会优雅地处理。他们将看到object.__init__()
并且不传递任何参数或传递所有参数,这两者都是不正确的。
底线:在没有实际调用该函数的情况下,没有通用的方法来发现函数调用是否合法。 **kwargs
是粗略的近似,并且在可变函数面前完全崩溃。 Variadic并不意味着“通过你喜欢的任何东西”;它意味着“规则过于复杂,无法在签名中表达。”因此,虽然在许多情况下可能会做你正在尝试做的事情,但总会出现没有正确答案的情况。
答案 2 :(得分:11)
你的问题在于你定义函数的方式,它应该像这样定义 -
def foo(**kwargs):
然后在函数内部,您可以迭代发送到函数的参数数量,如此 -
if kwargs is not None:
for key, value in kwargs.iteritems():
do something
你可以在这篇文章中找到更多关于使用** kwargs的信息 - http://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/
答案 3 :(得分:8)
您还可以使用decorator function过滤掉您的函数中不允许的关键字参数。您使用3.3中的新signature
函数返回函数Signature
from inspect import signature
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
sig = signature(func)
result = func(*[kwargs[param] for param in sig.parameters])
return result
return wrapper
从Python 3.0开始,您可以使用自{3.0版 以来不推荐使用的 要应用装饰现有函数,您需要将函数作为参数传递给装饰器: 演示: 要将装饰器应用于新功能,只需使用 您还可以使用任意关键字参数getargspec
import inspect
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
argspec = inspect.getargspec(func).args
result = func(*[kwargs[param] for param in argspec])
return result
return wrapper
>>> def foo(a, b):
... return a + b
...
>>> foo = decorator(foo)
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> foo(**d)
3
@
>>> @decorator
... def foo(a, b):
... return a + b
...
>>> foo(**d)
3
**kwargs
定义您的函数。>>> def foo(**kwargs):
... if 'a' in kwargs and 'b' in kwargs:
... return kwargs['a'] + kwargs['b']
...
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> foo(**d)
3
答案 4 :(得分:3)
我会做这样的事情:
def combine(function, dictionary):
return function(**{key:value for key, value in dictionary.items()
if key in inspect.getargspec(function)[0]}
)
使用:
>>> def this(a, b, c=5):
... print(a, b, c)
...
>>> combine(this, {'a': 4, 'b': 6, 'c': 6, 'd': 8})
4 6 6
>>> combine(this, {'a': 6, 'b': 5, 'd': 8})
6 5 5
答案 5 :(得分:2)
这仍在修改原始函数,但您可以在参数列表的末尾创建一个kwargs bitbucket:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=places"> </script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
</head>
<body>
<div>
<div id="my-modal" class="modal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<input name="keyword" id="keyword" type="text" />
</div>
</div>
</div>
</div>
<script>
$(function () {
$("#my-modal").modal({
show: false
}).on("shown", function () {
var input = document.getElementById("keyword");
var autocomplete = new google.maps.places.Autocomplete(input);
});
$('#my-modal').modal('show');
});
</script>
</body>
</html>