在Python argparse中,是否可以配对--no-something/ - 一些参数?

Omn*_*ous 33 python argparse python-3.x

我正在写一个程序,我希望有这样的参数:

--[no-]foo   Do (or do not) foo. Default is do.
Run Code Online (Sandbox Code Playgroud)

有没有办法让argparse为我这样做?

我正在使用Python 3.2

Omn*_*ous 22

那么,到目前为止,由于各种原因,所有答案都不能令人满意.所以这是我自己的答案:

class ActionNoYes(argparse.Action):
    def __init__(self, opt_name, dest, default=True, required=False, help=None):
        super(ActionNoYes, self).__init__(['--' + opt_name, '--no-' + opt_name], dest, nargs=0, const=None, default=default, required=required, help=help)
    def __call__(self, parser, namespace, values, option_string=None):
        if option_string.starts_with('--no-'):
            setattr(namespace, self.dest, False)
        else:
            setattr(namespace, self.dest, True)
Run Code Online (Sandbox Code Playgroud)

以及使用示例:

>>> p = argparse.ArgumentParser()
>>> p._add_action(ActionNoYes('foo', 'foo', help="Do (or do not) foo. (default do)"))
ActionNoYes(option_strings=['--foo', '--no-foo'], dest='foo', nargs=0, const=None, default=True, type=None, choices=None, help='Do (or do not) foo. (default do)', metavar=None)
>>> p.parse_args(['--no-foo', '--foo', '--no-foo'])
Namespace(foo=False)
>>> p.print_help()
usage: -c [-h] [--foo]

optional arguments:
  -h, --help       show this help message and exit
  --foo, --no-foo  Do (or do not) foo. (default do)
Run Code Online (Sandbox Code Playgroud)

遗憾的是,_add_action成员函数没有记录,因此在API支持方面,这不是"官方".另外,Action主要是持有者类.它本身的行为很少.如果可以使用它来更新自定义帮助消息,那将是很好的.例如--[no-]foo,在开头说.但是那部分是由课外的东西自动生成的Action.


hpa*_*ulj 17

v3.9 添加了一个action类来执行此操作。来自文档(靠近本节末尾action

可用 BooleanOptionalActionargparse添加了对布尔操作的支持,例如--foo--no-foo

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action=argparse.BooleanOptionalAction)
>>> parser.parse_args(['--no-foo'])
Namespace(foo=False)
Run Code Online (Sandbox Code Playgroud)

探索@wim 关于不相互排斥的评论。

In [37]: >>> parser = argparse.ArgumentParser()
    ...: >>> parser.add_argument('--foo', action=argparse.BooleanOptionalAction)
Out[37]: BooleanOptionalAction(option_strings=['--foo', '--no-foo'], dest='foo', nargs=0, const=None, default=None, type=None, choices=None, help=None, metavar=None)
Run Code Online (Sandbox Code Playgroud)

最后一行显示创建add_argument了一个BooleanOptionalActionAction 类。

具有各种输入:

In [38]: parser.parse_args('--foo'.split())
Out[38]: Namespace(foo=True)

In [39]: parser.parse_args('--no-foo'.split())
Out[39]: Namespace(foo=False)

In [40]: parser.parse_args([])
Out[40]: Namespace(foo=None)

In [41]: parser.parse_args('--no-foo --foo'.split())
Out[41]: Namespace(foo=True)
Run Code Online (Sandbox Code Playgroud)

因此,您可以提供两个标志,最后一个标志生效,覆盖前一个标志生成的任何内容。就好像我们定义了两个Actions具有相同dest但不同True/Falseconst 的 。

关键是它定义了两个标志字符串:

option_strings=['--foo', '--no-foo']
Run Code Online (Sandbox Code Playgroud)

这个新类的部分代码:

class BooleanOptionalAction(Action):
    def __init__(self,
                 option_strings,
                 dest,
                 ...):

        _option_strings = []
        for option_string in option_strings:
            _option_strings.append(option_string)

            if option_string.startswith('--'):
                option_string = '--no-' + option_string[2:]
                _option_strings.append(option_string)

     ...

    def __call__(self, parser, namespace, values, option_string=None):
        if option_string in self.option_strings:
            setattr(namespace, self.dest, not option_string.startswith('--no-'))
Run Code Online (Sandbox Code Playgroud)

因此该操作__init__定义了两个标志以及__call__no部分的检查。

  • 不幸的是,这似乎并不使它们相互排斥。 (2认同)

Zac*_*ung 6

是否add_mutually_exclusive_group()argparse帮助?

parser = argparse.ArgumentParser()
exclusive_grp = parser.add_mutually_exclusive_group()
exclusive_grp.add_argument('--foo', action='store_true', help='do foo')
exclusive_grp.add_argument('--no-foo', action='store_true', help='do not do foo')
args = parser.parse_args()

print 'Starting program', 'with' if args.foo else 'without', 'foo'
print 'Starting program', 'with' if args.no_foo else 'without', 'no_foo'
Run Code Online (Sandbox Code Playgroud)

以下是运行时的外观:

./so.py --help
usage: so.py [-h] [--foo | --no-foo]

optional arguments:
  -h, --help  show this help message and exit
  --foo       do foo
  --no-foo    do not do foo

./so.py
Starting program without foo
Starting program without no_foo

./so.py --no-foo --foo
usage: so.py [-h] [--foo | --no-foo]
so.py: error: argument --foo: not allowed with argument --no-foo
Run Code Online (Sandbox Code Playgroud)

这与互斥组中的以下不同,在程序中不允许任何选项(并且我假设您因语法而需要选项--).这意味着一个或另一个:

parser.add_argument('--foo=', choices=('y', 'n'), default='y',
                    help="Do foo? (default y)")
Run Code Online (Sandbox Code Playgroud)

如果这些是必需的(非选购),也许add_subparsers()正在使用你正在寻找的东西.

更新1

逻辑上不同,但可能更清洁:

...
exclusive_grp.add_argument('--foo', action='store_true', dest='foo', help='do foo')
exclusive_grp.add_argument('--no-foo', action='store_false', dest='foo', help='do not do foo')
args = parser.parse_args()

print 'Starting program', 'with' if args.foo else 'without', 'foo'
Run Code Online (Sandbox Code Playgroud)

运行它:

./so.py --foo
Starting program with foo
./so.py --no-foo
Starting program without foo
./so.py
Starting program without foo
Run Code Online (Sandbox Code Playgroud)


bte*_*tel 6

我修改了@Omnifarious的解决方案,使其更像标准操作:

import argparse

class ActionNoYes(argparse.Action):
    def __init__(self, option_strings, dest, default=None, required=False, help=None):

        if default is None:
            raise ValueError('You must provide a default with Yes/No action')
        if len(option_strings)!=1:
            raise ValueError('Only single argument is allowed with YesNo action')
        opt = option_strings[0]
        if not opt.startswith('--'):
            raise ValueError('Yes/No arguments must be prefixed with --')

        opt = opt[2:]
        opts = ['--' + opt, '--no-' + opt]
        super(ActionNoYes, self).__init__(opts, dest, nargs=0, const=None, 
                                          default=default, required=required, help=help)
    def __call__(self, parser, namespace, values, option_strings=None):
        if option_strings.startswith('--no-'):
            setattr(namespace, self.dest, False)
        else:
            setattr(namespace, self.dest, True)
Run Code Online (Sandbox Code Playgroud)

您可以像添加任何标准选项一样添加是/否参数。您只需要ActionNoYesaction参数中传递类:

parser = argparse.ArgumentParser()
parser.add_argument('--foo', action=ActionNoYes, default=False)
Run Code Online (Sandbox Code Playgroud)

现在,当您调用它时:

>> args = parser.parse_args(['--foo'])
Namespace(foo=True)
>> args = parser.parse_args(['--no-foo'])
Namespace(foo=False)
>> args = parser.parse_args([])
Namespace(foo=False)  
Run Code Online (Sandbox Code Playgroud)