以下代码的目的是将传递给main
的关键字参数传递给其他被调用的函数。但是,如果main
参数没有给key1
赋值,则key1
中的f2
的默认值会被None
的值覆盖main
。
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)
有没有一种简单的方法来改变只是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)
但我想知道是否还有其他选择。
答案 0 :(得分:1)
没有,因为当你打电话给f2
时,Python并不知道你传递的key1
是否被传入。例如,你可以这样做:
def main(x, key1=None, key2=None):
if some.otherCondition():
key1 = "Blah"
f2(x, key1=key1, key2=key2)
要完成您所描述的内容,Python必须检查key1
的每次使用,以查看它是否恰好等于默认值。如果您使用key1
做任何事情,除非将其直接传递给另一个函数,这很容易变得非常混乱。
此外,没有特别的理由为什么应该假设你想要忽略" key1
的值只是因为它恰好是main
中的默认值。同样可能的是,与main
类似的功能可能会专门用来覆盖f2
的默认值,并使用自己的默认值,例如:
def read_csv(filename, separator=","):
read_delimited_file(separator=separator)
def read_delimited_file(filename, separator="\t"):
# .,..
这里read_csv
的重点是覆盖它调用的函数中的默认分隔符,同时仍然允许用户在调用read_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()
聪明地自动化这一点,但是对于只有几个参数明确地做它更明确。)
看起来好像"愚蠢"必须这样做,但它并不是很愚蠢,因为这不是默认行为应该是什么。
答案 1 :(得分:0)
出现困难是因为main
为它调用的两个函数收集关键字参数,但并非所有关键字参数都被其中一个函数接受。如果在调用main
时指定了**kwargs
,则f1
函数无法使用key2
将单个字典传递给两个函数,而不会在main
中引发错误。
要解决此问题,main
的原始定义会手动处理关键字参数,以便仅将必要的参数传递给其他函数。这反过来导致“覆盖”被调用函数提供的默认值的问题。
一种选择是使用内省来定义一个函数,该函数将kwargs
中的main
子集化为main
调用的每个函数的适当字典。这避免了编写特定于每个函数的逻辑以对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, 42, None)
(1, None, None)
由于main
正在收集kwarg
字典,因此可以回避问题main
提供您真正希望由{{1}中调用的函数设置的关键字的默认值}。如果您确实希望传递给main
的{{1}}关键字值表示“将此关键字传递给其他函数时使用默认值”,则可以在{{1}中设置None
选项}}
main
输出:
omit_none
这种方法的灵感来自this关于SO的其他问题。 @BrenBarn的问题和评论有助于确定问题所在,谢谢!我会暂时搁置这个问题,看看其他人可能有什么不同的方法。