如何处理多个关键字参数? [英] How to deal with multiple keyword arguments?

查看:33
本文介绍了如何处理多个关键字参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got some trouble dealing with defining a user friendly function interface when passing two keyworded arguments with the same key.

问题

两个关键字参数具有相同键并且第二个关键字参数具有优先权的情况下,调用函数的最佳方法是什么?
如果发生此问题,第一个关键字参数总是来自 dict 中的解压缩数据库,而第二个关键字参数总是通过直接"将其作为关键字传递论据.
数据库字典值不得在函数的外部范围内被覆盖,因为它们可能会被多次使用.
为了保持功能对用户的可用性,首选后端实现.这意味着用户可以简单地将参数传递给函数,而无需使用其他模块,而函数本身可以完成所有魔术.

What is the best way to make it possible to call a function where two keyworded arguments have the same key and the second keyworded argument has precedence?
If this problem occurs, the first keyworded argument always stems from an unzipped database in a dict, while the second keyworded argument is always passed by giving it "directly" as a keyworded argument.
The database dictionary values must not be overwritten in the outer scopy of the functions, since they may be used multiple times.
edit: To keep up the usability of the function for the user, a backend-implementation is preferred. This means that the user can simply pass arguments to the function without the use of additional modules, while the function itself does all the magic.

问题

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

I've got a function, called fun_one here, which receives a multitude of arguments defined directly by the user of my program. This may be length and width of a heat exchanger for example. To ease the use of the function and make the calling code as short as possible, the use of databases is encouraged. These databases contain the data in a dict (or in a pandas Series), in this case called inputs.
To pass the database-dict inputs to the function, it is unzipped with **inputs and thus passed as keyworded arguments.
Now if the user wants to overwrite a specific argument of the database, my understanding of a user-friendly approach would be to just let him pass the preceded argument again, for example with length=23.7, and internally overwrite the argument from the database. But of course (see example code) this raises the error before I can even enter the function where I could try/except:

TypeError: fun_one() 为关键字参数length"获得了多个值

TypeError: fun_one() got multiple values for keyword argument '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.它检查 *argsdicts 并将 kwargs 中的值设置为 kwargs,如下面的代码示例所示.

My current solution fun_two involves not unzipping the database and passing it to *args. It checks *args for dicts and sets values which are not yet in kwargs to kwargs, as shown in the code example below.

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)

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

But this approach is imho quite obscure for the user and requires looping and checking at the beginning of quite alot functions, since this will apply to numerous functions. (I'll outsource it to a module, so it is one line per function. But it still deviates from my understanding of an easy and clear function definition).

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

Is there any better (more Pythonic) way to do this? Did I oversee some way to do it in the process of calling the function? Performance does not matter.
Thanks in advance!

推荐答案

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

Easiest solution is using ChainMap from collections (manual pages). That way you can chose which arguments have precedence. Example:

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:

If we call fun_one just with inputs:

c = ChainMap(inputs)
# or c = inputs
fun_one(**c)

输出将是:

{'width': 1.1, 'length': 15.8, 'some_other_args': 1}

来自手册:

A ChainMap 将多个字典或其他映射组合在一起以创建单一的、可更新的视图.如果未指定地图,则为单个空提供字典以便新链总是至少有一个映射.

A ChainMap groups multiple dicts or other mappings together to create a single, updateable view. If no maps are specified, a single empty dictionary is provided so that a new chain always has at least one mapping.

你可以在装饰器中包装这个ChainMap,一个变化是不要用**input调用fun_one(),只调用input:

You can wrap this ChainMap in decorator, one change is that don't call fun_one() with **input, only 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}

这篇关于如何处理多个关键字参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆