Python:命令行参数--foo和--no foo
对于使用Python内置的Python:命令行参数--foo和--no foo,python,boolean,command-line-arguments,argparse,Python,Boolean,Command Line Arguments,Argparse,对于使用Python内置的argparse包解析布尔命令行选项,我知道这个问题及其几个答案: 一些答案(没错,IMO)指出布尔选项最常见和最直接的习惯用法(从调用者的角度来看)是同时接受--foo和--no foo选项,这将程序中的一些值分别设置为True或False 然而,在我看来,我能找到的所有答案实际上并没有正确地完成任务。它们似乎通常不符合以下条件之一: 可以设置适当的默认值(True、False或None) 为program.py--Help提供的帮助文本正确且有用,包括显示默认值 其
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问题中显示的技术创建自定义操作:。这些操作会立即失败,原因是“error:argument--foo:expected one argument”
,或者(如果我设置nargs=0
)“ValueError:nargs for store actions必须大于0”
。从插入argparse
源代码来看,这似乎是因为预定义的“store\u const”、“store\u true”、“append”等之外的操作必须使用\u StoreAction
类,该类需要参数
有没有其他方法可以做到这一点?如果有人有一些我还没有想到的想法,请告诉我
(顺便说一句,我正在创建这个新问题,而不是试图添加到上面的第一个问题,因为上面的原始问题实际上是要求一个方法来处理--foo TRUE
和--foo FALSE
参数,这两个参数不同,我不太常见。)中的一个答案,特别是,包含从未被标准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)
(这段代码在Python2和Python3中都可以使用)
以下是实际情况:
$ 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
请注意,初始的用法
消息没有提到--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)
中的一个答案,特别是其中的一个答案,包含了一段从未被标准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)
(这段代码在Python2和Python3中都可以使用)
以下是实际情况:
$ 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
请注意,初始的用法
消息没有提到--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)
链接的
类customAction(argparse.Action)
子类Action
非\u StoreAction
。是商店\uuuu init\uuuu
抱怨nargs=0
。注意store\u true
subclassesstore\u const
.Update:看起来它现在已经被添加到argparse
库中:链接的类customAction(argparse.Action)
子类Action
非\u StoreAction
。是商店\uuuu init\uuuu
抱怨nargs=0
。注意store\u true
子类store\u const
。更新:看起来它现在已经被添加到argparse
库中:使用nargs=0
时,type=bool
是不必要的,不是吗?OP的第一个链接显示了使用此类型的陷阱
——唯一计算False
的字符串是空字符串。至于使用问题,始终可以选择提供自定义使用
参数。编写一个自动生成所需字符串的函数并不难。@hpaulj:是的,我认为在这里没有必要。我没有在arg列表中注意到它,因此没有将其拉出const=None
和choices=None
也是毫无意义的,因为这是默认设置。我刚刚编辑了一下。谢谢@torek,我会玩的。当然,将其集成到argparse
库本身是非常可取的,因此我也对您提到的问题进行了评论。对于nargs=0
来说,type=bool
是不必要的,不是吗?OP的第一个链接显示了使用此类型的陷阱
——唯一计算False
的字符串是空字符串。至于使用问题,始终可以选择提供自定义使用
参数。编写一个自动生成所需字符串的函数并不难。@hpaulj:是的,我认为这是不可能的