使用函数或方法的默认值,而不是argparse的默认值。

4

在使用argparse时,我没有找到一种优雅/DRY的方法来使用函数/方法默认值而不是由argparse传递的默认值。

例如,我有一些外部代码,我不想修改它。如何告诉argparse(或在argparse之后优雅地处理)如果用户没有在命令行上明确指定,则使用函数默认值?

import argparse

def foreign_func(fav_color="blue"):
    print(fav_color)

clparser = argparse.ArgumentParser()
clparser.add_argument(
    "--color",
    default=None,
    help="Enter color")
clargs = clparser.parse_args()

foreign_func(fav_color=clargs.color)

这将会输出 'None',而不是 "blue"
我的默认方法通常是这样的:
if clparser.color:
    foreign_func(fav_color=clargs.color)
else:
    foreign_func()

但这种方法看起来很笨拙,特别是当有多个命令行选项时。

编辑: 大家好,感谢您的快速反馈。为了澄清,虽然让argparse在其帮助中显示“blue”会很好,但那不是我的目标。

我正在寻找:

my_prog --color=red
>>> red

my_prog 
>>> blue

使用上述代码,程序输出的是“None”,而不是“blue”。

我认为,为了以一种优雅/DRY的方式实现你想要的功能,你必须检查外部代码,提取默认值,并通过枚举、类或类似的方式将它们传递给你的解析器。 - crissal
我和 @crissal 想法一致——例如,将 argparse.ArgumentParser 的默认值设置为外部函数的默认值。请查看 https://dev59.com/Omcs5IYBdhLWcg3wtmWf。 - buran
1
虽然在argparse帮助中显示foreign_func()的默认值会很好,但这不是我的主要目标。我的目标是避免编写大量样板代码以避免覆盖foreign_func()的默认值。 - JS.
1
你尝试过使用https://dev59.com/Omcs5IYBdhLWcg3wtmWf的答案吗?也就是使用inspect库? - Thomas Hilger
3个回答

3
你可以使用inspect来获取函数的默认参数值,并将其用作argparse代码中的默认值。
import argparse
import inspect

def foreign_func(fav_color="blue"):
    print(fav_color)

clparser = argparse.ArgumentParser()
clparser.add_argument(
    "--color",
    default=inspect.signature(foreign_func).parameters['fav_color'].default,
    help="Enter color")
clargs = clparser.parse_args()

foreign_func(fav_color=clargs.color)

请注意,如果函数没有默认值(如果它是必需参数),则这将表现得有点奇怪。在这种情况下,默认值将是inspect._empty作为一个标志值,而不是一个实际有意义的值。但是,您的代码似乎假设该参数始终是可选的,因此我也会按照这个假设进行处理。

1
我认为这种刮取默认值的做法很奇怪,因为整个设置默认值的目的就是为了不必传递值,而你却要显式地传递默认值。 - wim

2
您可以使用字典来按名称指定参数;如果缺少名称,则该参数将使用其默认值。示例:
def func(a=1,b=2):
    print("a=",a,"b=",b)

func()
func(**{})
func(**{'a':10})

将打印

a= 1 b= 2
a= 1 b= 2
a= 10 b= 2

基于这个解决方案,看起来你的想法的这个变体可能会起作用:foreign_func(**vars(clargs)) - JS.
1
这可以是一个不错的方法,特别是当与 argparse.SUPPRESS 结合使用时,后者会完全忽略在 args 命名空间中绑定可选值。对我而言,让函数提供默认值似乎更加清晰直接,而不是再次传入等于默认值的值(这个默认值最初必须从函数中获取)。 - wim
@wim 我发现的一个缺点是argparse选项名称必须直接映射到函数参数名称。在原始示例中,命令行选项名称必须完全映射到函数名称。 - JS.
2
@JS。在添加参数时,您可以使用“dest”kwarg来帮助处理。它允许您指定绑定到命名空间中的名称,而与在CLI中使用的名称无关。 - wim

1

结合@Scott Hunter、@wim和Adam Smith的回答和提示,以下解决方案似乎可行。

这个解决方案依赖于将argparse NameSpace对象转换为字典,然后在传递给 foreign_func 之前"分割"该字典。除了argparse.SUPPRESS选项外,传入的字典不会有缺失的命令行参数条目。这避免了对foreign_func默认值的"覆盖"。

注意:我没有测试过在foreign_func也有位置参数的情况下使用这种方法,所以可能会有差异。

import argparse

def foreign_func(fav_color="blue"):
    print(fav_color)

clparser = argparse.ArgumentParser()

clparser.add_argument(
    "--color",
    # Use 'dest' option if your command-line option name differs
    # from the function's argument name
    dest="fav_color",
    # 'argparse.SUPPRESS' will add an entry into the clargs "dict"
    # only if the option is passed on the command-line.
    default=argparse.SUPPRESS,
    help="Enter color")

clargs = clparser.parse_args()

print("DBG:", vars(clargs))
foreign_func(**vars(clargs))

测试:

python my_prog.py --color=red
>>> red

python my_prog.py 
>>> blue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接