Multiple positional arguments with Python and argparse

╄→гoц情女王★ 提交于 2019-12-02 23:47:12

srgerg was right about the definition of positional arguments. In order to get the result you want, You have to accept them as optional arguments, and modify the resulted namespace according to your need.

You can use a custom action:

class MyAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):

        # Set optional arguments to True or False
        if option_string:
            attr = True if values else False
            setattr(namespace, self.dest, attr)

        # Modify value of "input" in the namespace
        if hasattr(namespace, 'input'):
            current_values = getattr(namespace, 'input')
            try:
                current_values.extend(values)
            except AttributeError:
                current_values = values
            finally:
                setattr(namespace, 'input', current_values)
        else:
            setattr(namespace, 'input', values)

parser = argparse.ArgumentParser()
parser.add_argument('-a', nargs='+', action=MyAction)
parser.add_argument('-b', nargs='+', action=MyAction)
parser.add_argument('input', nargs='+', action=MyAction)

And this is what you get:

>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

Or you can modify the resulted namespace like this:

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', nargs='+')
>>> parser.add_argument('-b', nargs='+')
>>> parser.add_argument('input', nargs='+')
>>> result = parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])

>>> inputs = []
>>> inputs.extend(result.a)
>>> inputs.extend(result.b)
>>> inputs.extend(result.input)

>>> modified = argparse.Namespace(
        a = result.a != [],
        b = result.b != [],
        input = inputs)

And this is what you get:

>>> modified
Namespace(a=True, b=True, input=['filetwo', 'filethree', 'fileone'])

However, both method result in less readable and less maintainable code. Maybe it's better to change the program logic and do it in a different way.

You can't interleave the switches (i.e. -a and -b) with the positional arguments (i.e. fileone, filetwo and filethree) in this way. The switches must appear before or after the positional arguments, not in-between.

Also, in order to have multiple positional arguments, you need to specify the nargs parameter to add_argument. For example:

parser.add_argument('input', nargs='+')

This tells argparse to consume one or more positional arguments and append them to a list. See the argparse documentation for more information. With this line, the code:

parser.parse_args(['-a', '-b', 'fileone', 'filetwo', 'filethree'])

results in:

Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

The 'append' action makes more sense with an optional:

parser.add_argument('-i', '--input',action='append')
parser.parse_args(['-i','fileone', '-a', '-i','filetwo', '-b', '-i','filethree'])

You can interleave optionals with separate positionals ('input1 -a input2 -b input3'), but you cannot interleave optionals within one multiitem positional. But you can accomplish this with a two step parse.

import argparse
parser1 = argparse.ArgumentParser()
parser1.add_argument('-a', action='store_true')
parser1.add_argument('-b', action='store_true')
parser2 = argparse.ArgumentParser()
parser2.add_argument('input', nargs='*')

ns, rest = parser1.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']

ns = parser2.parse_args(rest, ns)
# Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

http://bugs.python.org/issue14191 is a proposed patch that will do this with single call to:

parser.parse_intermixed_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])

It seems to me that hpaulj is on the right track but making things a bit more complicated than necessary. I suspect that Blair is looking for something akin to the behavior of the old optparse module and doesn't really need the list of input arguments in the inputs field of the args object. He just wants

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', action='store_true')
parser.add_argument('-b', action='store_true')
opts, args = parser.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']

In the vernacular of optparse, the "options" are available in opts, and the list of possibly interspersed other "arguments" are in args.

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