Python 子parser argparse";错误:参数太少“;
上面的脚本Python 子parser argparse";错误:参数太少“;,python,python-2.7,argparse,Python,Python 2.7,Argparse,上面的脚本bird.py在Python3上可以正常工作。但在Python 2.7上,它不会解析参数: # bird.py from argparse import ArgumentParser, SUPPRESS parser = ArgumentParser(argument_default=SUPPRESS) parser.add_argument('--dead', action='store_true') subparsers = parser.add_subparsers() sub
bird.py
在Python3上可以正常工作。但在Python 2.7上,它不会解析参数:
# bird.py
from argparse import ArgumentParser, SUPPRESS
parser = ArgumentParser(argument_default=SUPPRESS)
parser.add_argument('--dead', action='store_true')
subparsers = parser.add_subparsers()
subparser = subparsers.add_parser('parrot', parents=[parser], add_help=False)
subparser.add_argument('--volts', type=int)
args = parser.parse_args()
print(args)
据我所知,代码中没有任何仅使用py3的特性。为什么不一样呢?如何更新此代码,使CLI也能支持Python 2.7,而无需以任何方式修改其Python 3行为?此错误来自
子parser.add_parser()
调用中的parser
父参数
$ python3 bird.py parrot --volts 4000000
Namespace(volts=4000000)
$ python2 bird.py parrot --volts 4000000
usage: bird.py parrot [-h] [--dead] [--volts VOLTS] {parrot} ...
bird.py parrot: error: too few arguments
然后
原因是使用子parser.add_parser()
创建一个新的解析器,调用ArgumentParser
的构造函数:
$ python scratch.py parrot --volts 1000
Namespace(dead=False, volts=1000)
调用
子parser.add_parser('parrot',parent=[parser],add_help=False)
将把根解析器参数复制到新的子parser参数中,包括子parser本身。这将导致无法解析的无限解析循环。父级
复制的数量超出了您的需要parents
尝试复制父对象的子parser,最终尝试使subparser
成为其自身的子parser。在Python3上,子parser默认为可选的,因此parrot
没有得到自己的parrot
这一事实并不是一个错误。在Python2上,子parser是必需的,因此parrot
需要自己的parrot
,并抱怨没有得到它。(考虑到您在尝试将父解析器用作父解析器时通过添加子解析器来修改父解析器,因此,如果更多的子解析器被损坏,我也不会感到惊讶。)
与其尝试将解析器
用作其自身子parser的父级,不如创建一个单独的父级解析器,并将其用作顶级解析器和parrot子parser的父级:
class ArgumentParser(_AttributeHolder, _ActionsContainer):
"""Object for parsing command line strings into Python objects.
Keyword Arguments:
- [...]
- parents -- Parsers whose arguments should be copied into this one
"""
结果:
from argparse import ArgumentParser
shared = ArgumentParser(add_help=False)
shared.add_argument('--dead', action='store_true')
parser = ArgumentParser(parents=[shared])
subparsers = parser.add_subparsers()
subparser = subparsers.add_parser('parrot', parents=[shared])
subparser.add_argument('--volts', type=int)
args = parser.parse_args()
print(args)
但是,还有更多的问题,因为您正在向父级和子级添加--dead
选项,并且它们试图写入相同的位置。孩子似乎优先考虑:
$ python2 bird.py parrot --volts 4000000
Namespace(dead=False, volts=4000000)
$ python3.6 bird.py parrot --volts 4000000
Namespace(dead=False, volts=4000000)
如果顶级解析器和子解析器具有不同的
dest
值,或者它们具有除store\u true
之外的其他操作,则为它们同时提供一个--dead
选项可能是有意义的,但是现在的方式没有多大意义。如果您想要不同的dest
值,我认为您不能使用parents
机制来实现这一点。parents
创建了一个递归子parser<代码>蟒蛇3堆栈53158180.py parrot parrot parrot工作正常。Py2也会递归,但仍然会遇到丢失的子Parser错误,就在这一行的后面。@hpaulj是的,我刚刚了解到,在完成快速调试会话并查看ArgumentParser的父参数的文档后,删除父参数是不可接受的,因为行为被修改了。查看python3 bird.py parrot--dead--volts 4000000
的区别。使用justdead
定义一个单独的父解析器,并将其用于主解析器和子解析器。在其他上下文中,使用“工作解析器”作为父级是有问题的。最新的Py3允许我们在add_subparsers
命令中指定required=True
,使其行为更像Py2(但一定要设置dest='cmd'
)。Py3有一个信息更丰富的缺少参数消息。在Py2中没有一种关闭所需子Parser的方法。是否真的设置了“--dead parrot--volts 200”dead=True”?我认为子解析器中的
default=False`会覆盖主解析器中设置的任何值。从用户的角度来看,定义一个在主parser和子parser中都有效的参数可能很好,但要以可靠的方式实现它却很困难。嗯,实际上,由于您所描述的原因,它在这个MCVE中不起作用。在实际情况中,我使用的是argparse.SUPPRESS
,我错误地认为这与此处无关-我已经更新了代码以制作更好的MCVE。您可以尝试使用argparse.py
的本地副本。py3版本应该在py2中运行,只需做几处更改(例如将成品从恢复为其原始形式)。
$ python2 bird.py parrot --volts 4000000
Namespace(dead=False, volts=4000000)
$ python3.6 bird.py parrot --volts 4000000
Namespace(dead=False, volts=4000000)
$ python bird.py --dead parrot
Namespace(dead=False, volts=None)
$ python bird.py parrot --dead
Namespace(dead=True, volts=None)