11

我正在使用 argparse 并尝试混合子命令和位置参数,并且出现了以下问题。

此代码运行良好:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

上面的代码将 args 解析为Namespace(positional='positional'),但是当我将位置参数更改为具有 nargs='?' 像这样:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional', nargs='?')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

它出错了:

usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional

为什么是这样?

4

4 回答 4

9

起初我认为与 jcollado 相同,但事实是,如果后续(顶级)位置参数具有特定的nargsnargs= Nonenargs= 整数),那么它会按您的预期工作。它在nargsis'?'或时失败'*',有时在 is 时失败'+'。因此,我深入研究了代码,以弄清楚发生了什么。

它归结为参数被拆分以供使用的方式。为了弄清楚谁得到了什么,调用parse_args汇总字符串中的参数,例如'AA',在您的情况下('A'对于位置参数,'O'对于可选),并最终生成一个正则表达式模式以与该汇总字符串匹配,具体取决于您的操作'已经通过.add_argumentand.add_subparsers方法添加到解析器中。

在每种情况下,例如,参数字符串最终都是'AA'. 要匹配的模式有什么变化(您可以在_get_nargs_patternin下看到可能的模式argparse.py。因为subpositional它最终是'(-*A[-AO]*)',这意味着允许一个参数后跟任意数量的选项或参数。对于positional,它取决于传递给的值nargs

  • None=>'(-*A-*)'
  • 3 => '(-*A-*A-*A-*)''-*A'每个预期参数一个)
  • '?'=>'(-*A?-*)'
  • '*'=>'(-*[A-]*)'
  • '+'=>'(-*A[A-]*)'

附加了这些模式,对于nargs=None(您的工作示例),您最终得到'(-*A[-AO]*)(-*A-*)',它匹配两个 group ['A', 'A']。这样,subpositional将只解析subpositional(你想要的),同时positional匹配它的动作。

但是,您nargs='?'最终会得到'(-*A[-AO]*)(-*A?-*)'. 第二组完全由可选模式组成,并且*是贪婪的,这意味着第一组 glob 字符串中的所有内容,最终识别出这两个组['AA', '']。这意味着subpositional得到两个参数,当然最终会窒息。

有趣的nargs='+''(-*A[-AO]*)(-*A[A-]*)', is 的模式只要你只传递一个参数就可以工作。说subpositional a,因为您需要在第二组中至少有一个位置参数。再次,由于第一组是贪婪的,传递subpositional a b c d得到你['AAAA', 'A'],这不是你想要的。

简而言之:一团糟。我想这应该被认为是一个错误,但不确定如果模式变成非贪婪模式会产生什么影响......

于 2011-12-29T18:41:54.003 回答
6
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs='?')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

print(parser.parse_args(['positional', 'subpositional']))
# -> Namespace(positional='positional')
print(parser.parse_args(['subpositional']))
# -> Namespace(positional=None)
parser.print_usage()
# -> usage: bpython [-h] [positional] {subpositional} ...

通常的做法是命令之前的参数(左侧)属于主程序,之后(右侧) -- 属于命令。因此positional应该走在命令之前subpositional。示例程序:git, twistd.

另外一个参数narg=?应该可能是一个选项(--opt=value),而不是一个位置参数。

于 2011-12-29T18:59:08.417 回答
5

我认为问题是当add_subparsers被调用时,在原始解析器中添加了一个新参数以传递子解析器的名称。

例如,使用以下代码:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')                                             
subparsers.add_parser('subpositional')                                             

parser.parse_args()

您将获得以下帮助字符串:

usage: test.py [-h] {subpositional} ... positional

positional arguments:
  {subpositional}
  positional

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

注意subpositional之前显示的positional。我会说你正在寻找的是在子解析器名称之前有位置参数。因此,您可能正在寻找的是在子解析器之前添加参数:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

parser.parse_args()

使用此代码获得的帮助字符串是:

usage: test.py [-h] positional {subpositional} ...

positional arguments:
  positional
  {subpositional}

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

这样,您首先将参数传递给主解析器,然后是子解析器的名称,最后是子解析器的参数(如果有的话)。

于 2011-12-29T17:23:13.777 回答
0

在 Python 3.5 中它仍然是一团糟。

我建议子类 ArgumentParser 保留所有剩余的位置参数,并在以后处理它们:

import argparse

class myArgumentParser(argparse.ArgumentParser):
    def parse_args(self, args=None, namespace=None):
       args, argv = self.parse_known_args(args, namespace)
       args.remaining_positionnals = argv
       return args

parser = myArgumentParser()

options = parser.parse_args()

剩余的位置参数在列表中options.remaining_positionals

于 2016-01-08T23:05:43.940 回答