是否可以将相同的可选参数传递给多个函数?

Is it possible to pass the same optional arguments to multiple functions?

我想问一下,在将相同的参数传递给函数的可选参数时,是否有一种方法可以防止不必要的重复代码。

希望下面的示例能很好地说明我正在尝试做的事情:

def f(arg1):
    def g(optional_1=0, optional_2=0, optional_3=0):
        return arg1+optional_1+optional_2+optional_3
    return g

b, c = 2, 3
f1 = f(1)
f2 = f(2)
calc_f1 = f1(optional_2=b, optional_3=c)
calc_f2 = f2(optional_2=b, optional_3=c)

如您所见,f1 和 f2 的区别仅在于传递给 f 的 arg1,之后我使用相同的变量调用它们以获得相同的可选参数。 当代码很短时没问题,但是当我有超过 10 个可选参数时,它就会变得不必要地冗长和冗余。 是否可以做类似

的事情
optional_variable_pair = #some way to combine them
calc_f1 = f1(optional_variable_pair)
calc_f2 = f2(optional_variable_pair)

所以我得到了更简洁易读的代码?

您使用键值对作为函数参数,为此您可以使用 *args 和 **kwargs:

optional_variable_pair = {
  "optional_1": 1,
  "optional_2": 2,
  "optional_3": 3,
}
calc_f1 = f1(**optional_variable_pair)
calc_f2 = f2(**optional_variable_pair)

任何带有多个可选参数的函数都有点臭,因为:

  1. 您得到的参数组合太多,需要进行大量测试。
  2. 由于所有的选项,函数必须有很多条件和路由,这增加了它的圈复杂度。

您可以应用重构将整个参数列表提取到一个对象中,并让函数在该对象上运行。如果您能找到一个统一的名称来描述您的参数列表并适合您在函数周围使用的任何隐喻,那么这将非常有效。您甚至可以反转调用,使函数成为对象的方法,从而获得一些封装。

回答你问的问题,答案是肯定的。使用关键字参数解包,您几乎可以完全按照自己的意愿行事。

def f(arg1):
    def g(optional_1=0, optional_2=0, optional_3=0):
        return arg1+optional_1+optional_2+optional_3
    return g

optional_variable_pair = {
    'optional_2': 2,
    'optional_3': 3
}

f1 = f(1)
f2 = f(2)
calc_f1 = f1(**optional_variable_pair)
calc_f2 = f2(**optional_variable_pair)

不过,如果我没看错你的意图,那么你问题的本质是想将具有相同连续参数的新第一个参数传递给一个函数。根据您的用例,包装函数 g 可能是不必要的。

def f(arg1, *, optional_1=0, optional_2=0, optional_3=0):
    return optional_1 + optional_2+optional_3

optional_variable_pair = {
    'optional_2': 2,
    'optional_3': 3
}

calc_f1 = f(1, **optional_variable_pair)
calc_f2 = f(2, **optional_variable_pair)

显然,如果第一个参数继续递增 1,则 for 循环是有序的。显然,如果您从不使用 optional_1 参数,则不需要包含它。但是,此外,如果您发现自己使用编号参数,那么您很有可能真的应该使用元组解包而不是关键字解包:

def f(*args):
    return sum(args)

optional_variable_pair = (2, 3)

for i in range(1, 3):
    calc = f(i, *optional_variable_pair)
    # ...do something with calc...

您可能也有兴趣研究 functools.partial,它可以代替您的包装函数 g,并允许这样做:

import functools

def f(*args):
    return sum(args)

f1 = functools.partial(f, 1)
f2 = functools.partial(f, 2)

calc_f1 = f1(2, 3) # = 1 + 2 + 3 = 6
calc_f2 = f2(2, 3) # = 2 + 2 + 3 = 7