我想使用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解析“假”,“F”,和他们的小写变体为假?


当前回答

简单快捷,但仅适用于参数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

其他回答

这适用于我所期望的一切:

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')

使用前面建议的另一个解决方案,但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错误报告。

在之前跟随@akash-desarda的优秀回答https://stackoverflow.com/a/59579733/315112后,通过lambda使用strtobool,后来,我决定直接使用strtobool。

import argparse
from distutils import util
parser.add_argument('--feature', type=util.strtobool)

是的,你是对的,strtobool返回的是int型,而不是bool型。但是strtobool将不会返回除0和1之外的任何其他值,而python将无缝一致地将它们转换为bool值。

>>> 0 == False
True
>>> 0 == True
False
>>> 1 == False
False
>>> 1 == True
True

当接收到错误的输入值时

python yours.py --feature wrong_value

一个argparse。与lambda相比,使用strtoool的Action将产生一个稍微清晰/可理解的错误消息:

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'

转换值:

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)

下面是另一种变体,没有额外的行/秒来设置默认值。布尔值总是被赋值,这样它就可以在逻辑语句中使用,而无需事先检查:

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.")