argparse.REMAINDER changes the behavior of positional arguments

笑着哭i 提交于 2019-12-11 04:37:42

问题


Without argparse.REMAINDER, optional arguments can be in front of or after positional arguments:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-a')
parser.add_argument('b')

print(parser.parse_args('-a 1 2'.split()))  # Namespace(a='1', b='2')
print(parser.parse_args('2 -a 1'.split()))  # Namespace(a='1', b='2')

But with argparse.REMAINDER, optional arguments must be in front:

parser.add_argument('c', nargs=argparse.REMAINDER)

print(parser.parse_args('-a 1 2 3'.split()))  # Namespace(a='1', b='2', c=['3'])
print(parser.parse_args('2 -a 1 3'.split()))  # Namespace(a=None, b='2', c=['-a', '1', '3'])

How can I parse the last line correctly, while argparse.REMAINDER is used?


回答1:


To add to kabanus's answer, it may help to know a bit about how arguments are parsed.

It iterates over the arguments, first looking for positionals, then optionals, then positionals, ...,

At the positionals step it tries to match as many as it can, using the nargs as a primary factor. The default is one string (your 'b'); '*' will match up to the next optional (the -a); but a REMAINDER ignores that constraint and matches to the end. So a 'positionals' evaluation is greedy, and one with REMAINDER is especially greedy.

So in the '2 -a 1 3' case, the initial '2' can match 'b', and the rest can match 'c'. Just one 'positionals' evaluation consumes the whole list, including the '-a', and it is done.

The documentation example shows this:

>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')

The '--foo' is handled as optional, but the '--arg1' is part of the REMAINDER. 'args' is filled immediately after 'command'.

If you want to retain control over when the REMAINDER is used, make it an optional, add_argument('-c',nargs='...'). Otherwise you are at the mercy of this positionals/optionals loop.

By the way, subparsers are implemented with a narg=argarse.PARSER, which is the name for '+...'. It's like REMAINDER but requires at least one string (the subparser name). It too consumes everything in its path.

Instead of REMAINDER you might want to use '*' and use '--' to trigger the 'consume everything else' action.




回答2:


From the documentation:

argparse.REMAINDER. All the remaining command-line arguments are gathered into a list. This is commonly useful for command line utilities that dispatch to other command line utilities:

this means using remainder by definition cannot have (any) other arguments after the argument accepting this, as they are part of the remainder, and would go into this argument.



来源:https://stackoverflow.com/questions/40280715/argparse-remainder-changes-the-behavior-of-positional-arguments

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!