Python 将关键字参数传递给关键字具有默认值的另一个函数
下面代码的目的是将传递给Python 将关键字参数传递给关键字具有默认值的另一个函数,python,Python,下面代码的目的是将传递给main的关键字参数传递给其他调用函数。但是,如果没有为key1参数main指定值,则f2中key1的默认值将被main传递的None值覆盖 def main(x, key1=None, key2=None): f1(x, key1=key1) f2(x, key1=key1, key2=key2) def f1(x, key1=None): # note key2 is absent print(x, key1) def f2(x, key1
main
的关键字参数传递给其他调用函数。但是,如果没有为key1
参数main
指定值,则f2
中key1
的默认值将被main
传递的None
值覆盖
def main(x, key1=None, key2=None):
f1(x, key1=key1)
f2(x, key1=key1, key2=key2)
def f1(x, key1=None): # note key2 is absent
print(x, key1)
def f2(x, key1=42, key2=None):
print(x, key1, key2)
main(1)
输出:
(1, None)
(1, None, None)
(1, 42, None)
(1, None, None)
(1, 42, None)
是否有一种简单的方法来更改main的定义(可能包括参数),以便调用函数的输出如下所示
(1, None)
(1, 42, None)
显然,有一些荒谬的方法可以仅仅改变main
来创建输出,但是在main
中插入逻辑来传递特定函数缺少的参数的默认值,或者根据main
传递的关键字中的None
来改变关键字是愚蠢的。所以我不是在寻找这样的解决方案
解决此问题的通常方法是在所有函数中使用**kwargs
。或者,您可以更改f2
的定义:
def f2(x, key1=None, key2=None):
if key1 is None:
key1 = 42
print(x, key1, key2)
但我想知道是否还有其他选择 没有,因为在调用
f2
时,Python不知道是否传入了作为key1
传递的内容。例如,您可以这样做:
def main(x, key1=None, key2=None):
if some.otherCondition():
key1 = "Blah"
f2(x, key1=key1, key2=key2)
为了实现您所描述的,Python必须检查key1
的每一次使用,以查看它是否恰好等于默认值。如果您使用key1
执行任何操作,除了直接将其传递给另一个函数之外,这很容易变得非常混乱
此外,没有特别的理由认为您想要“忽略”键1的值,因为它恰好是main
中的默认值。同样可能的是,类似于您的main
的函数可能被专门编写来用自己的默认值覆盖f2
的默认值,例如:
def read_csv(filename, separator=","):
read_delimited_file(separator=separator)
def read_delimited_file(filename, separator="\t"):
# .,..
这里,read\u csv
的全部要点是覆盖它调用的函数中的默认分隔符,同时仍然允许用户在调用read\u csv
时覆盖它
换句话说,无法回避的事实是,决定传递哪些参数涉及决策,您必须在代码中显式地实现这些决策。您似乎已经意识到几种可能的解决方案,您必须使用其中一种。我认为最清晰的方法之一就是创建一个参数字典:
def main(x, key1=None, key2=None):
kwargs = {}
if key1 is not None:
args['key1'] = key1
if key2 is not None:
args['key2'] = key2
f2(x, **kwargs)
(如果您有许多具有类似逻辑的参数,您可以开始巧妙地使用locals()
,在某种程度上实现自动化,但对于几个参数来说,显式地这样做更为明显。)
必须这样做可能看起来很“愚蠢”,但其实并不愚蠢,因为默认行为应该是这样的并不明显。原始代码中的问题
出现困难的原因是main
为它调用的两个函数收集关键字参数,但并非所有关键字参数都被其中一个函数接受。如果调用main
时指定了key2
,则main
函数无法使用**kwargs
将一个dict传递给两个函数,而不会在f1
中引发错误
为了解决这个问题,main
的原始定义手动处理关键字参数,以便只将必要的参数传递给其他函数。这反过来会导致“覆盖”被调用函数提供的默认值的问题
可能的解决办法
一种选择是使用内省来定义一个函数,该函数将main
中的kwargs
子集为main
调用的每个函数的适当dict。这避免了将特定于每个函数的逻辑写入dict子集
此更改允许main
调用的函数在其定义中显式列出接受的关键字参数,并允许main
在将其交给其他函数之前收集所有关键字参数,而无需设置默认值
def kwargs_for_func(func, kwargs, omit_none=False):
arg_count = func.func_code.co_argcount
func_kwargs = func.func_code.co_varnames[:arg_count]
new_kwargs = {}
for k in kwargs:
if k not in func_kwargs:
continue
val = kwargs[k]
if not (omit_none and val is None):
new_kwargs[k] = val
return new_kwargs
def apply_with_valid_kwargs(func, *args, **kwargs):
return func(*args, **kwargs_for_func(func, kwargs))
def main(*args, **kwargs):
apply_with_valid_kwargs(f1, *args, **kwargs)
apply_with_valid_kwargs(f2, *args, **kwargs)
def f1(x, key1=None):
pass # different def to emphasize changed output from f2
def f2(x, key1=42, key2=None):
print(x, key1, key2)
main(1) # f2 prints (1, 42, None)
main(1, key1=None) # f2 prints (1, None, None)
输出:
(1, None)
(1, None, None)
(1, 42, None)
(1, None, None)
(1, 42, None)
由于main
正在收集kwarg
dict,这就避免了main
为确实需要由main
中调用的函数设置的关键字提供默认值的问题。如果您确实希望传递给main
的None
关键字值表示“将此关键字传递给其他函数时使用默认值”,则可以在kwargs\u for_func
中设置ommit\u None
选项
def main(x, **kwargs):
f1(x, **kwargs_for_func(f1, kwargs, True))
f2(x, **kwargs_for_func(f2, kwargs, True))
main(1, key1=None)
输出:
(1, None)
(1, None, None)
(1, 42, None)
(1, None, None)
(1, 42, None)
笔记
这种方法受到了其他问题的启发@BrenBarn的问题和评论有助于确定问题所在,谢谢!我将把这个问题留一段时间,看看其他人可能有什么不同的方法 在您设想的方案中,如果用户调用
main(1,None)
(为key1
显式传递None),会发生什么情况?我不明白您为什么要将默认值放在f1和f2中。由于main提供了默认值,这些变量将始终具有值。@SBHayes:因为这些函数可能会在其他位置调用,而不仅仅是从main
@BrenBarn:问得好。根据它的实现方式,可能存在一些模糊性。老实说,在这种情况下,无论行为是什么,拥有一个能起作用的东西都是很好的。但是我不希望任何人在main
中显式地将值设置为None
,因为函数本身是一个包装器。