argparse.REMAINDER changes the behavior of positio

2019-07-25 11:07发布

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?

2条回答
不美不萌又怎样
2楼-- · 2019-07-25 11:38

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.

查看更多
Emotional °昔
3楼-- · 2019-07-25 11:39

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.

查看更多
登录 后发表回答