对于使用Python内置的argparse
包解析布尔型命令行选项的情况,我知道这个问题及其几个答案:Parsing boolean values with argparse。
几个答案(正确地,IMO)指出(从调用者的角度来看)布尔选项最常见,最直接的习惯用法是同时接受--foo
和--no-foo
选项,将程序中的某些值分别设置为True
或False
。
但是,在我看来,我能找到的所有答案实际上并未正确完成任务。他们似乎通常无法满足以下条件之一:
True
,False
或None
)。program.py --help
给出的帮助文本是正确且有用的,包括显示默认设置。--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 custom actions with additional arguments passed中显示的技术来创建自定义动作。这些失败后立即说出"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不太常见。)
答案 0 :(得分:2)
your linked question中的一个答案,特别是Robert T. McGibbon中的一个答案,包含来自an enhancement request的代码片段,该片段从未被标准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)