当使用相同的键传递两个关键字参数时,我在定义一个用户友好的函数接口时遇到了一些困难。
问题
怎样才能调用一个函数,其中两个关键字参数具有相同的键,而第二个关键字参数具有优先
如果出现此问题,第一个关键字参数总是来自于dict
中解压缩的数据库,而第二个关键字参数总是以“直接”作为关键字参数来传递。
数据库字典值不能在函数的外部范围内被覆盖,因为它们可能被多次使用。
编辑:为了保持该函数对用户的可用性,首选使用backend-implementation。这意味着用户可以简单地将参数传递给函数,而不需要使用其他模块,而函数本身则具有所有的魔力。
问题
这里有一个名为fun_one
的函数,它接收由我的程序的用户直接定义的大量参数。例如,这可能是换热器的length
和width
。为了简化函数的使用,使调用代码尽可能短,鼓励使用数据库。这些数据库包含dict
(或熊猫系列)中的数据,在本例中称为inputs
。
为了将数据库-dict
inputs
传递给函数,它使用**inputs
解压缩,从而作为关键字参数传递。
现在如果用户想要覆盖数据库的一个特定参数,我对用户友好方法的理解就是让他再次传递前面的参数,例如使用length=23.7
,然后在内部覆盖数据库中的参数。但是,当然(参见示例代码),这会在我输入可以try/except
的函数之前引发错误。
TypeError: fun_one()获得关键字参数'length‘的多个值
代码示例复制错误
def fun_one(*args, **kwargs): # short example function
print(kwargs)
inputs = {'length': 15.8, 'width': 1.1, 'some_other_args': np.random.rand(3)}
fun_one(**inputs, length=23.7)
我的当前解决方案
我当前的解决方案fun_two
涉及到,而不是解压缩数据库并将其传递给*args
。它检查*args
是否为dict
,并将尚未在kwargs
中设置的值设置为kwargs
,如下面的代码示例所示。
def fun_two(*args, **kwargs): # example function printing kwargs
print(kwargs) # print kwargs before applying changes
for arg in args: # find dicts
if type(arg) is dict:
for key, val in arg.items(): # loop over dict
_ = kwargs.setdefault(key, val) # set val if key not in dict
print(kwargs) # print kwargs after applying changes
inputs = {'length': 15.8, 'width': 1.1, 'some_other_args': np.random.rand(3)}
fun_two(inputs, length=23.7)
但是对于用户来说,这种方法是相当模糊的,需要在相当多的函数开始时进行循环和检查,因为这将适用于许多函数。(我将把它外包给一个模块,因此每一个函数都是一行。但它仍然偏离了我对一个简单而明确的函数定义的理解)。
有什么更好的方法(更多的毕加索)来做这件事吗?在调用函数的过程中,我是否监督了某种方法?表现并不重要。
提前感谢!
发布于 2018-07-16 05:46:46
最简单的解决方案是使用ChainMap
from collections
(手册页)。这样,您就可以选择哪些参数具有优先级。示例:
from collections import ChainMap
def fun_one(*args, **kwargs): # short example function
print(kwargs)
inputs = {'length': 15.8, 'width': 1.1, 'some_other_args': 1}
c = ChainMap({'length': 23.7}, inputs) # we overwrite length here
fun_one(**c)
产出:
{'some_other_args': 1, 'width': 1.1, 'length': 23.7}
如果我们只使用输入调用fun_one:
c = ChainMap(inputs)
# or c = inputs
fun_one(**c)
产出将是:
{'width': 1.1, 'length': 15.8, 'some_other_args': 1}
来自手册:
ChainMap将多个dicts或其他映射组合在一起,以创建单个可更新的视图。如果没有指定映射,则提供一个空字典,以便新链始终至少有一个映射。
您可以将此ChainMap封装在装饰器中,一个更改是不使用**input
调用fun_one()
,只调用input
from collections import ChainMap
def function_with_many_arguments(func):
orig_func = func
def _f(*args, **kwargs):
if args:
c = ChainMap(kwargs, args[0])
return orig_func(**c)
else:
return orig_func(*args, **kwargs)
return _f
@function_with_many_arguments
def fun_one(*args, **kwargs): # short example function
print(kwargs)
inputs = {'length': 15.8, 'width': 1.1, 'some_other_args': 1}
fun_one(inputs, length=23)
指纹:
{'some_other_args': 1, 'length': 23, 'width': 1.1}
发布于 2018-07-16 06:34:20
作为Andrej答案的扩展(再次感谢!),我添加了一个ChainMap
s循环,以便在同一个函数中使用多个数据库,并能够使用各种位置参数。多个数据库的优先级是先到先得的,但在这种情况下这是可以的。这位是装修师:
def function_with_many_arguments(func):
orig_func = func
def _f(*args, **kwargs):
if args:
c = ChainMap(kwargs)
for arg in args:
if type(arg) is dict:
c = ChainMap(c, arg)
orig_func(*args, **c)
else:
orig_func(*args, **kwargs)
return _f
下面是我扩展的示例函数,它有一些代码来测试它。我只是加入了所有的随机论点,没有考虑到任何毕不过的方法.;)
@function_with_many_arguments
def fun_one(a, b, *args, name, database=None, **kwargs):
print(name)
print(a, b)
print(kwargs)
inputs = {'length': 15.8, 'width': 1.1, 'some_other_args': np.random.rand(3)}
inputs2 = inputs.copy()
inputs2['width'] = 123
inputs2['weight'] = 3.8
fun_one(4, 8, inputs, database=inputs2, name='abc', length=23.8, weight=55)
https://stackoverflow.com/questions/51363208
复制