繁体   English   中英

Python:命令行参数--foo和--no-foo

[英]Python: command-line arguments --foo and --no-foo

对于使用Python的内置argparse包解析布尔命令行选项的情况,我知道这个问题及其几个答案: 用argparse解析布尔值

几个答案(正确地,IMO)指出,布尔选项(从调用者的角度来看)最常见,最直接的习惯用法是接受--foo--no-foo选项,这会在分别设置为TrueFalse

但是,在我看来,我能找到的所有答案实际上都无法正确完成任务。 他们似乎通常无法满足以下条件之一:

  1. 可以设置合适的默认值( TrueFalseNone )。
  2. program.py --help提供的帮助文本是正确且有用的,包括显示默认值。
  3. 两者之一(我并不在乎哪一个,但有时两者都是可取的):
    • 参数--foo可以被后面的参数--no-foo覆盖,反之亦然;
    • --foo--no-foo不兼容且互斥。

我想知道的是,使用argparse是否完全可以做到这一点。

根据@mgilson和@fnkr的回答,这是我最近到达的地点:

def add_bool_arg(parser, name, help_true, help_false, default=None, exclusive=True):
    if exclusive:
        group = parser.add_mutually_exclusive_group(required=False)
    else:
        group = parser
    group.add_argument('--' + name, dest=name, action='store_true', help=help_true)
    group.add_argument('--no-' + name, dest=name, action='store_false', help=help_false)
    parser.set_defaults(**{name: default})


parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
add_bool_arg(parser, 'foo', "Do foo", "Don't foo", exclusive=True)
add_bool_arg(parser, 'bar', "Do bar", "Don't bar", default=True, exclusive=False)

多数情况下,这做得很好,但帮助文本令人困惑:

usage: argtest.py [-h] [--foo | --no-foo] [--bar] [--no-bar]

optional arguments:
  -h, --help  show this help message and exit
  --foo       Do foo (default: None)
  --no-foo    Don't foo (default: None)
  --bar       Do bar (default: True)
  --no-bar    Don't bar (default: True)

更好的帮助文本如下所示:

usage: argtest.py [-h] [--foo | --no-foo] [--bar] [--no-bar]

optional arguments:
  -h, --help      show this help message and exit
  --foo --no-foo  Whether to foo (default: None)
  --bar --no-bar  Whether to bar (default: True)

但是我看不到实现此目标的方法,因为必须始终将“-*”和“ --no- *”声明为单独的参数(对吗?)。

除了上述SO问题的建议外,我还尝试使用另一个SO问题中显示的技术来创建自定义动作: Python argparse自定义动作,并传递了其他参数 它们立即失败,或者说"error: argument --foo: expected one argument" ,或者(如果我设置nargs=0"ValueError: nargs for store actions must be > 0" 从进入argparse源看,这看起来像是因为除预定义的“ store_const”,“ store_true”,“ append”等之外的其他动作必须使用_StoreAction类,该类需要一个参数。

还有其他方法可以做到这一点吗? 如果某人有我尚未想到的各种想法,请告诉我!

(顺便说一句,我正在创建这个新问题,而不是试图添加到上面的第一个问题,因为上面的原始问题实际上是在要求一种方法来处理--foo TRUE--foo FALSE参数,这是不同的, IMO较少见。)

您所链接问题中的一个答案,特别是Robert T. McGibbon的一个答案,包括一个来自增强请求的代码段,而该代码段从未被标准argparse接受。 但是,如果您不介意烦恼,它的效果会很好。 这是我的复制品,并做了一些小的修改,作为一个独立的模块,其中添加了一些pydoc字符串,以及其用法示例:

import argparse
import re

class FlagAction(argparse.Action):
    """
    GNU style --foo/--no-foo flag action for argparse
    (via http://bugs.python.org/issue8538 and
    https://stackoverflow.com/a/26618391/1256452).

    This provides a GNU style flag action for argparse.  Use
    as, e.g., parser.add_argument('--foo', action=FlagAction).
    The destination will default to 'foo' and the default value
    if neither --foo or --no-foo are specified will be None
    (so that you can tell if one or the other was given).
    """
    def __init__(self, option_strings, dest, default=None,
                 required=False, help=None, metavar=None,
                 positive_prefixes=['--'], negative_prefixes=['--no-']):
        self.positive_strings = set()
        # self.negative_strings = set()
        # Order of strings is important: the first one is the only
        # one that will be shown in the short usage message!  (This
        # is an annoying little flaw.)
        strings = []
        for string in option_strings:
            assert re.match(r'--[a-z]+', string, re.IGNORECASE)
            suffix = string[2:]
            for positive_prefix in positive_prefixes:
                s = positive_prefix + suffix
                self.positive_strings.add(s)
                strings.append(s)
            for negative_prefix in negative_prefixes:
                s = negative_prefix + suffix
                # self.negative_strings.add(s)
                strings.append(s)
        super(FlagAction, self).__init__(option_strings=strings, dest=dest,
                                         nargs=0, default=default,
                                         required=required, help=help,
                                         metavar=metavar)

    def __call__(self, parser, namespace, values, option_string=None):
        if option_string in self.positive_strings:
            setattr(namespace, self.dest, True)
        else:
            setattr(namespace, self.dest, False)


if __name__ == '__main__':
    p = argparse.ArgumentParser()
    p.add_argument('-a', '--arg', help='example')
    p.add_argument('--foo', action=FlagAction, help='the boolean thing')
    args = p.parse_args()
    print(args)

(此代码在Python 2和3中均适用)。

这是正在发生的事情:

$ python flag_action.py -h
usage: flag_action.py [-h] [-a ARG] [--foo]

optional arguments:
  -h, --help         show this help message and exit
  -a ARG, --arg ARG  example
  --foo, --no-foo    the boolean thing

请注意,初始usage消息未提及--no-foo选项。 除了使用您不喜欢的分组方法之外,没有其他简单的方法可以纠正此问题。

$ python flag_action.py -a something --foo
Namespace(arg='something', foo=True)
$ python flag_action.py --no-foo
Namespace(arg=None, foo=False)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM