这个问题在http://bugs.python.org/issue9334中有深入讨论。大部分活动发生在2011年。我去年添加了一个补丁,但是argparse
的积压补丁很多。
问题在于像'--env'
或"-s WHATEVER -e COOL STUFF"
这样的字符串,在跟随一个需要参数的选项后可能存在歧义。
optparse
做一个简单的从左到右的解析。第一个--env
是一个带一个参数的选项标志,因此它会消耗下一个,无论它看起来像什么。另一方面,argparse
会两次循环通过这些字符串。首先,它将它们分类为'O'或'A'(选项标志或参数)。在第二个循环中,它使用类似于正则表达式的模式匹配来处理可变的nargs
值。在这种情况下,看起来我们有OO
,即两个标志而没有参数。
在使用argparse
时的解决方案是确保参数字符串不会被误认为是选项标志。这里(和Bug问题中)显示的可能性包括:
--env="--env"
--env " --env"
--env "--env "
--env "--env one two"
'--env'
本身看起来像一个标志(即使用引号括起来,参见
sys.argv
),但是当跟随其他字符串时它不是。但是
"-env one two"
有问题,因为它可以被解析为
['-e','nv one two']
,一个`'-e'`标志后面跟着一个字符串(甚至更多的选项)。
--
和
nargs=argparse.PARSER
也可以用于强制
argparse
将所有后续字符串视为参数。但是它们仅在参数列表末尾起作用。
在issue9334中提出了一个补丁来添加
args_default_to_positional=True
模式。在这种模式下,只有当解析器能够清楚地将字符串与定义的参数匹配时,才将字符串分类为选项标志。因此,在“--env --one”中,“--one”将被分类为参数。但是“--env --env”中的第二个“--env”仍然会被分类为选项标志。
进一步扩展相关案例,请参阅:
Using argparse with argument values that begin with a dash ("-").
parser = argparse.ArgumentParser(prog="PROG")
parser.add_argument("-f", "--force", default=False, action="store_true")
parser.add_argument("-e", "--extra")
args = parser.parse_args()
print(args)
生产
1513:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra='--foo one', force=False)
1513:~/mypy$ python3 stack16174992.py --extra "-foo one"
usage: PROG [-h] [-f] [-e EXTRA]
PROG: error: argument -e/--extra: expected one argument
1513:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra='-bar one', force=False)
1514:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra='one', force=True)
"-foo one" 的情况失败了,因为 -foo
被解释为 -f
标志加上未指定的额外内容。这个行为与允许 -fe
被解释为 ['-f', '-e']
相同。
如果我将 nargs
改为 REMAINDER
(而不是 PARSER
),则 -e
后面的所有内容都会被解释为该标志的参数:
parser.add_argument("-e", "--extra", nargs=argparse.REMAINDER)
所有情况都可行。请注意,值是一个列表。不需要引号:
1518:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra=['--foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-foo one"
Namespace(extra=['-foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra=['-bar one'], force=False)
1519:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra=['one'], force=True)
1520:~/mypy$ python3 stack16174992.py --extra --foo one
Namespace(extra=['--foo', 'one'], force=False)
1521:~/mypy$ python3 stack16174992.py --extra -foo one
Namespace(extra=['-foo', 'one'], force=False)
argparse.REMAINDER
类似于'*',但它会获取接下来的所有内容,不管它看起来是否像一个标志。而argparse.PARSER
更像是'+', 它期望首先传递一个位置参数。这是由subparsers
使用的nargs
。
对于REMAINDER
的使用已经在文档中有所记录,可以参考https://docs.python.org/zh-cn/3/library/argparse.html#nargs
argparse
(或Python解释器)有机会判断它们是否存在之前,引号已经被shell消耗掉了。 - Charles Duffy