如何在Python中编码argparse组合选项

24 投票
3 回答
26382 浏览
提问于 2025-04-16 15:23

我一直在为完成这小任务而烦恼。我做了一些实验,但没有得到想要的结果。

需求:

   test2.py [-c/-v] -f

使用说明或规则:

  1. -c(比较)需要两个参数。

    -v(验证)需要一个参数。

    这两个选项中必须有一个存在,但不能同时使用。

  2. -f 是一个必填参数(输出文件名)。

输出:

我能够得到如下所示的期望输出

kp@kp:~/Study/scripts$ ./test.py -c P1 P2 -f p
kp@kp:~/Study/scripts$ ./test.py -v P1 -f p
kp@kp:~/Study/scripts$ ./test.py -v P1 
usage: test.py <functional argument> <ouput target argument>
test.py: error: argument -f/--file is required
kp@kp:~/Study/scripts$ ./test.py -c P1 P2 
usage: test.py <functional argument> <ouput target argument>
test.py: error: argument -f/--file is required
kp@kp:~/Study/scripts$ 

问题是:

当你使用 test.py -h 时,
1. 输出不会说明 -c/-v 中的任意一个是必需的,但不能同时使用。它显示所有参数都是可选的。
2. 输出会把 -f 选项放在可选参数下,这不对。-f 是必填参数,我想把它显示在可选参数之外。

如何修改脚本,使得 -h 选项的输出对用户更友好(不需要任何外部验证)

usage: test.py <functional argument> <ouput target argument>

Package Compare/Verifier tool.

optional arguments:
  -h, --help            show this help message and exit
  -f outFileName, --file outFileName
                        File Name where result is stored.
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.
kiran@kiran-laptop:~/Study/scripts$ 

代码:

我正在使用下面的代码来实现输出,

#!/usr/bin/python

import sys
import argparse

def main():
    usage='%(prog)s <functional argument> <ouput target argument>'
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(usage=usage,description=description)

    parser.add_argument('-f','--file',action='store',nargs=1,dest='outFileName',help='File Name where result is stored.',metavar="outFileName",required=True)


    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-c','--compare',action='store',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',action='store',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')
    args = parser.parse_args()

if __name__ == "__main__":
    main()

3 个回答

4

我觉得大家主要抱怨的是默认的 位置参数可选参数 的分组名称。在 帮助 文档里,可选参数 是指:需要像 -f 或 --file 这样的标志; 而 位置参数 是指 通过位置来识别。对于默认值来说,位置参数 确实是必须的,而 可选参数 确实是可选的(不是必须的)。不过,用户可以通过 required 属性来改变这一点,这就导致了术语上的混淆。

解决这个问题的一种方法是定义你自己的参数组。这些组会影响 帮助 文档的布局,但不会影响参数解析。它们也不会影响 使用 行。

def main():
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(usage=None,description=description)

    maingroup = parser.add_argument_group(title='required')
    maingroup.add_argument('-f','--file',nargs=1,dest='outFileName',help='File Name where result is stored.',metavar="outFileName",required=True)
    exgroup = parser.add_argument_group(title='one or the other')

    group = exgroup.add_mutually_exclusive_group(required=True)
    group.add_argument('-c','--compare',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')
    args = parser.parse_args()

会产生:

usage: stack5603364.py [-h] -f outFileName (-c Package1 Package2 | -v Package)

Package Compare/Verifier tool.

optional arguments:
  -h, --help            show this help message and exit

required:
  -f outFileName, --file outFileName
                        File Name where result is stored.

one or the other:
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.

mutually_exclusive_group 只影响 使用 行。

(-c Package1 Package2 | -v Package)

显示一个需要选择其中一个选项的组。

[-c Package1 Package2 | -v Package]

会是一个可选组。[] 用来标记可选参数(在“不是必须的”意义上)。注意 -h 仍然被标记。

http://bugs.python.org/issue9694 是相关的 Python 问题,argparse 的作者更倾向于这种 argument_group 的方法。

31

将文件名设置为一个位置参数,让 argparse 自己生成使用说明信息:

$ python so.py --help
usage: so.py [-h] [-c Package1 Package2 | -v Package] outFileName

文件名应该是一个位置参数,并且你应该让 argparse 自动写出它自己的使用说明。

代码

#!/usr/bin/python

import sys
import argparse

def main():
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(description=description,
                                     epilog='--compare and --verify are mutually exclusive')

    parser.add_argument('f',action='store',nargs=1,
                        help='File Name where result is stored.',
                        metavar="outFileName")

    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('-c','--compare',action='store',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',action='store',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')

    args = parser.parse_args()

if __name__ == "__main__":
    main()

帮助信息

$ python so.py --help
usage: so.py [-h] [-c Package1 Package2 | -v Package] outFileName

Package Compare/Verifier tool.

positional arguments:
  outFileName           File Name where result is stored.

optional arguments:
  -h, --help            show this help message and exit
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.

--compare and --verify are mutually exclusive
11

你想要的具体输出是什么呢?我不知道有什么标准的写法可以在帮助信息中表示互斥关系,如果你自己编一个,用户可能会觉得很困惑。而且我猜想argparse这个库也不支持这种写法(要是支持的话,早就能用了)。

我建议你保持简单,直接在每个参数的帮助信息中解释一下它们是互斥的。所以可以把它们的帮助字符串改成这样:

-c Package1 Package2, --compare Package1 Package2
                      Compare two packages (may not be used with -v).
-v Package, --verify Package
                      Verify Content of package (may not be used with -c).

这样写非常明显,而且也比较简洁。

另外一个选择是干脆不提这个问题,让用户自己去发现这些参数是互斥的,用户在同时使用它们时会看到一个友好的错误提示,比如“PROG: error: argument -c: not allowed with argument -v”。

撰写回答