使用 argparse 解析布尔值
- 2025-01-16 08:38:00
- admin 原创
- 111
问题描述:
我想使用 argparse 来解析写为“--foo True”或“--foo False”的布尔命令行参数。例如:
my_program --my_boolean_flag False
但是,以下测试代码并没有实现我想要的效果:
import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", type=bool)
cmd_line = ["--my_bool", "False"]
parsed_args = parser.parse(cmd_line)
遗憾的是,parsed_args.my_bool
计算结果为True
。即使我将 改为 ,情况也是如此cmd_line
,["--my_bool", ""]
这令人惊讶,因为bool("")
计算结果为False
。
我如何让 argparse 解析"False"
、"F"
以及它们的小写变体False
?
解决方案 1:
我认为更规范的方法是:
command --feature
和
command --no-feature
argparse
很好地支持这个版本:
Python 3.9+:
parser.add_argument('--feature', action=argparse.BooleanOptionalAction)
Python <3.9:
parser.add_argument('--feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
当然,如果您确实想要--arg <True|False>
版本,您可以将其ast.literal_eval
作为“类型”或用户定义的函数传递...
def t_or_f(arg):
ua = str(arg).upper()
if 'TRUE'.startswith(ua):
return True
elif 'FALSE'.startswith(ua):
return False
else:
pass #error condition maybe?
解决方案 2:
还有一种解决方案使用了以前的建议,但出现了“正确”的解析错误argparse
:
def str2bool(v):
if isinstance(v, bool):
return v
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
这对于使用默认值进行开关非常有用;例如
parser.add_argument("--nice", type=str2bool, nargs='?',
const=True, default=False,
help="Activate nice mode.")
允许我使用:
script --nice
script --nice <bool>
并仍使用默认值(特定于用户设置)。该方法的一个(间接相关的)缺点是“nargs”可能会捕获位置参数 - 请参阅此相关问题和此 argparse 错误报告。
解决方案 3:
如果你想同时允许--feature
(--no-feature
最后一个获胜)
这允许用户使用 创建 shell 别名--feature
,并使用 覆盖它--no-feature
。
Python 3.9 及更高版本
parser.add_argument('--feature', default=True, action=argparse.BooleanOptionalAction)
Python 3.8 及更低版本
我推荐 mgilson 的回答:
parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
如果你不想允许--feature
,--no-feature
同时
您可以使用互斥组:
feature_parser = parser.add_mutually_exclusive_group(required=False)
feature_parser.add_argument('--feature', dest='feature', action='store_true')
feature_parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
如果你要设置其中许多,你可以使用这个助手:
def add_bool_arg(parser, name, default=False):
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('--' + name, dest=name, action='store_true')
group.add_argument('--no-' + name, dest=name, action='store_false')
parser.set_defaults(**{name:default})
add_bool_arg(parser, 'useful-feature')
add_bool_arg(parser, 'even-more-useful-feature')
解决方案 4:
这是另一种没有额外行来设置默认值的变体。布尔值始终被分配,因此可以在逻辑语句中使用它而无需事先检查:
import argparse
parser = argparse.ArgumentParser(description="Parse bool")
parser.add_argument("--do-something", default=False, action="store_true",
help="Flag to do something")
args = parser.parse_args()
if args.do_something:
print("Do something")
else:
print("Don't do something")
print(f"Check that args.do_something={args.do_something} is always a bool.")
解决方案 5:
一句话:
parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
解决方案 6:
最简单&最正确的方法是:
from distutils.util import strtobool
parser.add_argument('--feature', dest='feature',
type=lambda x: bool(strtobool(x)))
请注意,True 值为 y、yes、t、true、on 和 1;false 值为 n、no、f、false、off 和 0。如果 val 为其他值,则会引发 ValueError。
解决方案 7:
似乎对type=bool
和 的type='bool'
含义有些困惑。其中一个(或两个)应该表示“运行函数 ” bool()
,还是“返回布尔值”?就目前而言,它type='bool'
没有任何意义。 add_argument
给出'bool' is not callable
错误,就像您使用type='foobar'
、 或一样type='int'
。
但argparse
确实有注册表允许您定义这样的关键字。它主要用于action
,例如“action='store_true'”。您可以使用以下命令查看已注册的关键字:
parser._registries
显示一本字典
{'action': {None: argparse._StoreAction,
'append': argparse._AppendAction,
'append_const': argparse._AppendConstAction,
...
'type': {None: <function argparse.identity>}}
定义了很多动作,但只有一种类型,即默认类型argparse.identity
。
此代码定义了一个“bool”关键字:
def str2bool(v):
#susendberg's function
return v.lower() in ("yes", "true", "t", "1")
p = argparse.ArgumentParser()
p.register('type','bool',str2bool) # add type keyword to registries
p.add_argument('-b',type='bool') # do not use 'type=bool'
# p.add_argument('-b',type=str2bool) # works just as well
p.parse_args('-b false'.split())
Namespace(b=False)
parser.register()
没有记录,但也没有隐藏。大多数情况下,程序员不需要知道它,因为type
和action
接受函数和类值。有很多 stackoverflow 示例为两者定义自定义值。
如果前面的讨论不明显,bool()
这并不意味着“解析字符串”。摘自 Python 文档:
bool(x):使用标准真值测试程序将值转换为布尔值。
对比一下
int(x):将数字或字符串 x 转换为整数。
解决方案 8:
一种非常相似的方法是使用:
feature.add_argument('--feature',action='store_true')
如果你在命令中设置了参数 --feature
command --feature
该参数将为 True,如果您未设置类型 --feature,则参数默认值始终为 False!
解决方案 9:
我正在寻找同样的问题,在我看来,最好的解决方案是:
def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
并使用它将字符串解析为布尔值,如上所述。
解决方案 10:
简单又安全。
from ast import literal_eval
parser.add_argument('--boolean_flag',
help='This is a boolean flag.',
type=literal_eval,
choices=[True, False],
default=True)
编辑有几个人评论说使用内置功能不安全eval
。这在脚注中有所说明。但考虑到人们有时会从 SO 中复制粘贴,最好有更安全、更正确的答案。
解决方案 11:
这符合我所期望的一切:
add_boolean_argument(parser, 'foo', default=True)
parser.parse_args([]) # Whatever the default was
parser.parse_args(['--foo']) # True
parser.parse_args(['--nofoo']) # False
parser.parse_args(['--foo=true']) # True
parser.parse_args(['--foo=false']) # False
parser.parse_args(['--foo', '--nofoo']) # Error
代码:
def _str_to_bool(s):
"""Convert string to bool (in argparse context)."""
if s.lower() not in ['true', 'false']:
raise ValueError('Need bool; got %r' % s)
return {'true': True, 'false': False}[s.lower()]
def add_boolean_argument(parser, name, default=False):
"""Add a boolean argument to an ArgumentParser instance."""
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--' + name, nargs='?', default=default, const=True, type=_str_to_bool)
group.add_argument('--no' + name, dest=name, action='store_false')
解决方案 12:
除了@mgilson所说的之外,还应该注意,还有一种ArgumentParser.add_mutually_exclusive_group(required=False)
方法可以使执行这一点变得简单--flag
,并且--no-flag
不会同时使用。
解决方案 13:
更简单的方法是使用如下所示的方法。
parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
解决方案 14:
这实际上已经过时了。对于 Python 3.7+,Argparse 现在支持布尔参数(搜索 BooleanOptionalAction)。
实现如下:
import argparse
ap = argparse.ArgumentParser()
# List of args
ap.add_argument('--foo', default=True, type=bool, help='Some helpful text that is not bar. Default = True')
# Importable object
args = ap.parse_args()
还有一件事需要提及:这将通过 argparse.ArgumentTypeError 阻止除 True 和 False 之外的所有参数条目。如果您出于任何原因想要尝试更改这一点,您可以为此创建一个自定义错误类。
解决方案 15:
之前关注过 @akash-desarda 的优秀回答https://stackoverflow.com/a/59579733/315112后,决定使用strtobool
via ,后来我决定直接lambda
使用。strtobool
import argparse
from distutils import util
parser.add_argument('--feature', type=util.strtobool)
是的,您说得对,strtobool
返回的是int
,而不是bool
。但除了和strtobool
之外不会返回任何其他值,并且 python 会将它们无缝且一致地转换为值。0
`1`bool
>>> 0 == False
True
>>> 0 == True
False
>>> 1 == False
False
>>> 1 == True
True
在收到错误的输入值时
python yours.py --feature wrong_value
argparse.Action 与strtobool
相比lambda
会产生稍微更清晰/易懂的错误消息:
yours.py: error: argument --feature: invalid strtobool value: 'wrong_value'
与此代码相比,
parser.add_argument('--feature', type=lambda x: bool(util.strtobool(x))
这将产生一个不太清晰的错误信息:
yours.py: error: argument --feature: invalid <lambda> value: 'wrong_value'
解决方案 16:
扩展 gerardw 的答案
parser.add_argument("--my_bool", type=bool)
不起作用的原因bool("mystring")
是True
对于任何非空字符串bool("False")
来说都是如此True
。
你想要的是
我的程序.py
import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument(
"--my_bool",
choices=["False", "True"],
)
parsed_args = parser.parse_args()
my_bool = parsed_args.my_bool == "True"
print(my_bool)
$ python my_program.py --my_bool False
False
$ python my_program.py --my_bool True
True
$ python my_program.py --my_bool true
usage: my_program.py [-h] [--my_bool {False,True}]
my_program.py: error: argument --my_bool: invalid choice: 'true' (choose from 'False', 'True')
解决方案 17:
其实非常简单。不需要导入任何包。
记住这type=func
意味着返回 func(x),像type=bool -> bool(x)
这样:
parser.add_argument('--feature', default=False, type=lambda x: x == 'True')
现在我们知道了原理,你也可以将其扩展到type=lambda x: x.lower() not in ['false', 'no', '0', 'none', ...]
。
解决方案 18:
最简单的方法是使用选择:
parser = argparse.ArgumentParser()
parser.add_argument('--my-flag',choices=('True','False'))
args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)
不传递 --my-flag 则结果为 False。如果您始终希望用户明确指定选择,则可以添加required=True选项。
解决方案 19:
理解argparse
和布尔参数
当使用argparse
处理布尔命令行参数时,指定 的type=bool
行为可能与您预期的不同。无论您传递还是 ,type=bool
参数始终会将提供的值评估为。True
`True`False
例如:
parser.add_argument("--my_bool", type=bool, default=False)
当你运行时:
my_program --my_bool False
arg.my_bool
将被设置为True
,而不是False
,因为任何非空字符串参数(包括“False”)都True
被 Python 的bool()
函数解释为 。
处理布尔标志的正确方法
要正确处理布尔标志,请使用action
带有store_true
或 的参数store_false
。这样,标志的存在将值设置为True
,而标志的不存在将值设置为False
。
例如:
parser.add_argument("--my_boolean_flag", action="store_true", help="Set this flag to enable feature X")
如果
--my_boolean_flag
在命令行中提供了,arg.my_boolean_flag
将是True
。如果
--my_boolean_flag
没有提供,arg.my_boolean_flag
则为False
。
示例用法
为了清楚起见,这里有一个完整的例子:
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--my_boolean_flag", action="store_true"(argparse.BooleanOptionalAction), help="Enable the boolean flag")
args = parser.parse_args()
print(args.my_boolean_flag)
运行
my_program
会输出False
。运行
my_program --my_boolean_flag
会输出True
。
解决方案 20:
我认为最规范的方式是:
parser.add_argument('--ensure', nargs='*', default=None)
ENSURE = config.ensure is None
解决方案 21:
快速而简单,但仅适用于参数 0 或 1:
parser.add_argument("mybool", default=True,type=lambda x: bool(int(x)))
myargs=parser.parse_args()
print(myargs.mybool)
从终端调用后,输出将为“False”:
python myscript.py 0
解决方案 22:
class FlagAction(argparse.Action):
# From http://bugs.python.org/issue8538
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()
for string in option_strings:
assert re.match(r'--[A-z]+', string)
suffix = string[2:]
for positive_prefix in positive_prefixes:
self.positive_strings.add(positive_prefix + suffix)
for negative_prefix in negative_prefixes:
self.negative_strings.add(negative_prefix + suffix)
strings = list(self.positive_strings | self.negative_strings)
super(FlagAction, self).__init__(option_strings=strings, dest=dest,
nargs=0, const=None, default=default, type=bool, choices=None,
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)
解决方案 23:
出现此问题的原因是type=bool
inargparse
无法按预期工作。任何非空字符串(如"False"
OR 1
OR abcd123
)都像在 Python 中一样进行评估True
。
选项 1:自定义函数
使用自定义函数明确处理True
和False
值:
import argparse
def str2bool(value):
if isinstance(value, bool):
return value
if value.lower() in {'true', 't', 'yes', '1'}:
return True
elif value.lower() in {'false', 'f', 'no', '0'}:
return False
else:
raise argparse.ArgumentTypeError(f'Invalid boolean value: {value}')
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", type=str2bool, default=False, help="Boolean flag")
cmd_line = ["--my_bool", "False"]
parsed_args = parser.parse_args(cmd_line)
print(parsed_args.my_bool) # Output: False
这将运行为
python my_program.py --my_bool True
# Output: True
python my_program.py --my_bool False
# Output: False
选项 2:使用store_true
或store_false
- 你会发现这更常见
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", action="store_true", help="Set flag to True")
parser.add_argument("--no_bool", action="store_false", dest="my_bool", help="Set flag to False")
cmd_line = ["--my_bool"]
parsed_args = parser.parse_args(cmd_line)
print(parsed_args.my_bool) # Output: True
这将运行如下:
python my_program.py --my_bool
# Output: True
python my_program.py --no_bool
# Output: False
但不确定性能如何
解决方案 24:
转换值:
def __arg_to_bool__(arg):
"""__arg_to_bool__
Convert string / int arg to bool
:param arg: argument to be converted
:type arg: str or int
:return: converted arg
:rtype: bool
"""
str_true_values = (
'1',
'ENABLED',
'ON',
'TRUE',
'YES',
)
str_false_values = (
'0',
'DISABLED',
'OFF',
'FALSE',
'NO',
)
if isinstance(arg, str):
arg = arg.upper()
if arg in str_true_values:
return True
elif arg in str_false_values:
return False
if isinstance(arg, int):
if arg == 1:
return True
elif arg == 0:
return False
if isinstance(arg, bool):
return arg
# if any other value not covered above, consider argument as False
# or you could just raise and error
return False
[...]
args = ap.parse_args()
my_arg = options.my_arg
my_arg = __arg_to_bool__(my_arg)
解决方案 25:
您可以创建一个 BoolAction 然后使用它
class BoolAction(Action):
def __init__(
self,
option_strings,
dest,
nargs=None,
default: bool = False,
**kwargs,
):
if nargs is not None:
raise ValueError('nargs not allowed')
super().__init__(option_strings, dest, default=default, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
input_value = values.lower()
b = input_value in ['true', 'yes', '1']
if not b and input_value not in ['false', 'no', '0']:
raise ValueError('Invalid boolean value "%s".)
setattr(namespace, self.dest, b)
然后action=BoolAction
开始parser.add_argument()