关于python:Argparse – 不要用`nargs’捕获位置参数。

Argparse - do not catch positional arguments with `nargs`.

我正在尝试编写一个函数wo,您可以通过argparse解析变量数量的参数-我知道我可以通过nargs="+"来实现这一点。不幸的是,argparse帮助的工作方式(以及人们通常在cli中编写参数的方式)将位置参数放在最后。这导致我的位置参数作为可选参数的一部分被捕获。

1
2
3
4
5
6
7
8
#!/usr/bin/python
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("positional", help="my positional arg", type=int)
parser.add_argument("-o","--optional", help="my optional arg", nargs='+', type=float)
args = parser.parse_args()
print args.positional, args.optional

./test.py -h的形式运行,显示以下使用说明:

1
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] positional

但是如果我运行./test.py -o 0.21 0.11 0.33 0.13 100给了我

1
test.py: error: too few arguments

为了正确解析args,我必须运行./test.py 100 -o 0.21 0.11 0.33 0.13

我该怎么办:

  • 使argparse重新格式化使用率输出,以减少误导,甚至更好:

  • 告诉argparse不要捕获可选参数-o的最后一个元素(如果它是列表中的最后一个元素)


有一个关于这个的bug报告:http://bugs.python.org/issue9338

argparse optionals with nargs='?', '*' or '+' can't be followed by positionals

一个简单的(用户)修复方法是使用--将过账与期权分开:

1
./test.py -o 0.21 0.11 0.33 0.13 -- 100

我写了一个补丁,它保留了一些由位置使用的参数。但这不是一件小事。

至于更改使用行-最简单的方法是自己写,例如:

1
2
usage: test.py [-h] positional [-o OPTIONAL [OPTIONAL ...]]
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] -- positional

我不建议向usage格式化程序添加逻辑来进行这种更改。我认为这会变得太复杂。

另一个快速修复方法是将这个位置转换成(必需的)可选的。它为用户提供了关于他们的订单的完全自由,并可能减少混乱。如果您不想混淆"必选"选项,只需给它一个逻辑默认值。

1
2
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] -p POSITIONAL
usage: test.py [-h] [-o OPTIONAL [OPTIONAL ...]] [-p POS_WITH_DEFAULT]

帮助格式化程序的一个简单更改是按照定义的顺序简单地列出参数。修改格式化程序行为的正常方法是对其进行子类化,并更改一两个方法。这些方法中的大多数都是"private"(前缀),因此,您需要认识到未来的代码可能会发生变化(缓慢)。

在这种方法中,actions是参数列表,按定义的顺序排列。默认行为是将"optionals"从"positionals"中拆分,并将列表与末尾的位置重新组合。还有一些额外的代码可以处理需要包装的长行。通常它将位置放在单独的行上。我忽略了这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Formatter(argparse.HelpFormatter):
    # use defined argument order to display usage
    def _format_usage(self, usage, actions, groups, prefix):
        if prefix is None:
            prefix = 'usage: '

        # if usage is specified, use that
        if usage is not None:
            usage = usage % dict(prog=self._prog)

        # if no optionals or positionals are available, usage is just prog
        elif usage is None and not actions:
            usage = '%(prog)s' % dict(prog=self._prog)
        elif usage is None:
            prog = '%(prog)s' % dict(prog=self._prog)
            # build full usage string
            action_usage = self._format_actions_usage(actions, groups) # NEW
            usage = ' '.join([s for s in [prog, action_usage] if s])
            # omit the long line wrapping code
        # prefix with 'usage:'
        return '%s%s

'
% (prefix, usage)

parser = argparse.ArgumentParser(formatter_class=Formatter)

它产生一个使用行,比如:

1
usage: stack26985650.py [-h] positional [-o OPTIONAL [OPTIONAL ...]]

不要使用nargs="+",考虑使用action="append"。这需要在每个数字前面传递-o,但除非您真正想要,否则它不会使用参数。