Python:命令行参数 --foo 和 --no-foo
Python: command-line arguments --foo and --no-foo
对于使用 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
class,这需要一个参数。
有没有其他方法可以做到这一点?如果有人有我还没有想到的想法组合,请告诉我!
(顺便说一句-我正在创建这个新问题,而不是尝试添加到上面的第一个问题,因为上面的原始问题实际上是在寻求一种方法来处理 --foo TRUE
和 --foo FALSE
参数,这是不同的,IMO 不太常见。)
your linked question, specifically the one by Robert T. McGibbon, includes a code snippet from 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
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)
对于使用 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
class,这需要一个参数。
有没有其他方法可以做到这一点?如果有人有我还没有想到的想法组合,请告诉我!
(顺便说一句-我正在创建这个新问题,而不是尝试添加到上面的第一个问题,因为上面的原始问题实际上是在寻求一种方法来处理 --foo TRUE
和 --foo FALSE
参数,这是不同的,IMO 不太常见。)
your linked question, specifically the one by Robert T. McGibbon, includes a code snippet from 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
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)