当我处理也可以从命令行解析的模块的默认参数时,如何避免冗余?

How can I avoid redundancy when I handle default arguments to modules that may also be parsed from the command line?

我正在尝试找到一种好方法来避免我的 python 模块出现冗余(这可能会带来分歧的风险),该模块可以从命令行执行。

考虑这样一个 python 模块:

#!/usr/bin/env python2.7
from argparse import ArgumentParser

DEFAULT_A_ARG='alpha'

def funky(a=DEFAULT_A_ARG, b=False):
    pass

if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument('-a', default=DEFAULT_A_ARG)
    parser.add_argument('-b', action='store_true', default=False)
    args = parser.parse_args()
    funky(a=args.a, b=args.b)

因此,使用此模块的人可以将其导入到自己的代码中或从命令行调用它。我不喜欢在函数定义和命令行解析器之间为 'a' 参数指定默认值的冗余。除了审美上的烦恼,我倾向于担心,在可能由许多开发人员通过版本控制编辑的较大模块中,可以更改其中一个默认值,但不能更改另一个。

有没有什么好的方法可以消除在两个不同地方指定默认值的冗余?

首先,定义 funky 以使用 None 作为默认参数:

def funky(a=None, b=None):
    if a is None:
        a = DEFAULT_A_ARG
    if b is None:
        b = False

然后,让 None 也成为每个命令行参数的默认值:

if __name__ == '__main__':
    parser = ArgumentParser()
    # You probably don't have to specify a default of None explicitly
    parser.add_argument('-a', default=None)
    parser.add_argument('-b', action='store_true', default=None)
    args = parser.parse_args()
    funky(a=args.a, b=args.b)

如果要更改实际的默认值,您只需在一个地方(funky 的主体内)而不是两个地方(在 -a/[ 的定义中)更改它们=17=] 并在参数列表中 funky).

或者,如果您使用 ArgumentDefaultsHelpFormatter 通过用法消息显示默认值,您可以这样做:

#!/usr/bin/env python2.7
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

parser = ArgumentParser(prog='my_program',
                        formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('-a', default='alpha', help='your favorite greek letter')
parser.add_argument('-b', action='store_true', help='print numbers in binary format')

def funky(a=parser.get_default('a'), b=parser.get_default('b')):
    pass

if __name__ == '__main__':
    args = parser.parse_args()
    funky(**args.__dict__)

在终端上:

$ my_program.py --help
usage: my_program [-h] [-a A] [-b]

optional arguments:
  -h, --help  show this help message and exit
  -a A        your favorite Greek letter (default: alpha)
  -b          print number in binary format (default: False)

你定义一次并在三个​​不同的地方得到它,没有常量或污染函数体...


编辑

我不太了解恢复函数元数据。因此,阅读文档 对回答这个问题很有帮助。如果可以,看看标准库的inspect模块...

这里,另一种可能性,在函数定义中分配参数默认值:

#!/usr/bin/env python2.7
from argparse import ArgumentParser

def funky(x, a='alpha', b=False):
    pass

if __name__ == '__main__':
    import argparse
    import inspect

    funky_spec = inspect.getargspec(funky)
    defaults = funky_spec.defaults
    defaults = dict(zip(funky_spec.args[-len(defaults):], defaults))
    # effect: ['x', 'a', 'b'] + ('alpha', False) -> {'a': 'alpha', 'b': False}

    parser = argparse.ArgumentParser()
    parser.add_argument('x', help='input number')
    parser.add_argument('-a', default=defaults['a'])
    parser.add_argument('-b', action='store_true', default=defaults['b'])
    args = parser.parse_args()

    funky(x=args.x, a=args.a, b=args.b)

在 Python 3 中,"inspect" 块会更清晰一些:

[...]

    funky_params = inspect.signature(funky).parameters.values()
    defaults = {p.name: p.default for p in funky_params if p.default is not p.empty}

[...]