如何处理多个关键字参数?

Sco*_*y1- 3 python arguments function

当使用相同的 key 传递两个关键字参数时,我在定义用户友好的函数接口时遇到了一些麻烦。

问题

调用两个关键字参数具有相同键并且第二个关键字参数具有优先级的函数的最佳方法是什么?
如果出现此问题,第一个关键字参数始终来自a 中的解压缩数据库dict,而第二个关键字参数始终通过“直接”将其作为关键字参数来传递。
数据库字典值不得在函数的外部副本中被覆盖,因为它们可能会被多次使用。
编辑:为了保持用户功能的可用性,首选后端实现。这意味着用户可以简单地将参数传递给函数,而无需使用其他模块,而函数本身则完成所有的魔力。


问题

我有一个函数,fun_one在此处调用,它接收由程序用户直接定义的多个参数。例如,这可以是length热交换器width。为了方便函数的使用并使调用代码尽可能短,鼓励使用数据库。dict这些数据库包含(或 pandas 系列)中的数据,在本例中称为inputs.
为了将database-传递dict inputs给函数,它被解压缩**inputs并作为关键字参数传递。
现在,如果用户想要覆盖数据库的特定参数,我对用户友好方法的理解是让他再次传递前面的参数,例如使用length=23.7,并在内部覆盖数据库中的参数。但是当然(请参阅示例代码)这会在我什至可以输入我可以输入的函数之前引发错误try/except

类型错误: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)
Run Code Online (Sandbox Code Playgroud)

我目前的解决方案

我当前的解决方案fun_two涉及不解压缩数据库并将其传递到*args. 它检查*argss并将尚未包含在todict中的值设置为,如下面的代码示例所示。 kwargskwargs

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)
Run Code Online (Sandbox Code Playgroud)

但这种方法对于用户来说相当晦涩,并且需要在很多函数的开头进行循环和检查,因为这将适用于许多函数。(我将它外包给一个模块,所以每个函数一行。但这仍然偏离了我对简单清晰的函数定义的理解)。

有没有更好(更Pythonic)的方法来做到这一点?我在调用函数的过程中是否监督了某种方法来做到这一点?性能并不重要。
提前致谢!

And*_*ely 6

最简单的解决方案是使用ChainMapcollections手册)。这样您就可以选择哪些参数具有优先权。例子:

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)
Run Code Online (Sandbox Code Playgroud)

输出:

{'some_other_args': 1, 'width': 1.1, 'length': 23.7}
Run Code Online (Sandbox Code Playgroud)

如果我们仅使用输入调用 fun_one:

c = ChainMap(inputs)
# or c = inputs
fun_one(**c)
Run Code Online (Sandbox Code Playgroud)

输出将是:

{'width': 1.1, 'length': 15.8, 'some_other_args': 1}
Run Code Online (Sandbox Code Playgroud)

来自手册:

ChainMap将多个字典或其他映射组合在一起以创建单个可更新的视图。如果未指定映射,则提供一个空字典,以便新链始终具有至少一个映射。

你可以将这个 ChainMap 包装在装饰器中,一个变化是不调用fun_one()with **input,只调用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)
Run Code Online (Sandbox Code Playgroud)

印刷:

{'some_other_args': 1, 'length': 23, 'width': 1.1}
Run Code Online (Sandbox Code Playgroud)