【问题标题】:Argparse optional boolean [duplicate]Argparse 可选布尔值 [重复]
【发布时间】:2019-02-23 11:07:19
【问题描述】:

我正在尝试获得以下行为:

  • python test.py⟹店铺foo=False
  • python test.py --foo⟹店铺foo=True
  • python test.py --foo bool⟹店铺foo=bool

当我使用它时它会起作用

parser.add_argument('--foo', nargs='?', default=False, const=True)

但是,如果我添加 type=bool,它会中断,试图强制强制转换为布尔值。在这种情况下

python test.py --foo False

实际上最终存储了foo=True。怎么回事??

【问题讨论】:

标签: python python-3.x argparse


【解决方案1】:

确定你需要那个模式吗? --foo--foo <value> 一起用于布尔开关,不是常用的模式。

至于您的问题,请记住命令行值是一个字符串type=bool 表示您希望应用bool(entered-string-value)。对于--foo False,这意味着bool("False"),产生True;所有非空字符串都是真的!参见Why is argparse not parsing my boolean flag correctly?

不支持--foo / --foo <string value>,我强烈建议您使用--foo 表示True,删除参数值,而是添加--no-foo 选项显式设置False:

parser.add_argument('--foo', default=False, action='store_true')
parser.add_argument('--no-foo', dest='foo', action='store_false')

--no-foo 开关上的dest='foo' 添加确保它存储的False 值(通过store_false)最终使用相同的args.foo 属性。

从 Python 3.9 开始,您还可以使用 argparse.BooleanOptionalAction 操作类:

parser.add_argument("--foo", action=argparse.BooleanOptionalAction)

它会有同样的效果,处理--foo--no-foo来设置和清除标志。

如果您有一些其他配置机制可以将foo 设置为True,并且您需要使用命令行开关再次覆盖它,您只需要--foo / --no-foo 组合。 --no-<option> 是一种广泛采用的标准,用于反转布尔命令行开关。

如果您没有特别需要 --no-foo 倒置开关(因为只是 省略 --foo 已经意味着“错误”),那么只需坚持使用action='store_true' 选项。这使您的命令行简单明了!

但是,如果您的用例或其他限制条件特别要求您的命令行必须有一些 --foo (true|false|0|1) 支持之王,那么添加您自己的转换器:

def str_to_bool(value):
    if isinstance(value, bool):
        return value
    if value.lower() in {'false', 'f', '0', 'no', 'n'}:
        return False
    elif value.lower() in {'true', 't', '1', 'yes', 'y'}:
        return True
    raise ValueError(f'{value} is not a valid boolean value')

parser.add_argument('--foo', type=str_to_bool, nargs='?', const=True, default=False)
  • const 值用于nargs='?' 参数,其中参数值被省略。当使用--foo 时,此处设置foo=True
  • default=False 用于完全不使用开关时。
  • type=str_to_bool 用于处理--foo <value> 的情况。

演示:

$ cat so52403065.py
from argparse import ArgumentParser

parser = ArgumentParser()

def str_to_bool(value):
    if value.lower() in {'false', 'f', '0', 'no', 'n'}:
        return False
    elif value.lower() in {'true', 't', '1', 'yes', 'y'}:
        return True
    raise ValueError(f'{value} is not a valid boolean value')

parser.add_argument('--foo', type=str_to_bool, nargs='?', const=True, default=False)

print(parser.parse_args())
$ python so52403065.py
Namespace(foo=False)
$ python so52403065.py --foo
Namespace(foo=True)
$ python so52403065.py --foo True
Namespace(foo=True)
$ python so52403065.py --foo no
Namespace(foo=False)
$ python so52403065.py --foo arrbuggrhellno
usage: so52403065.py [-h] [--foo [FOO]]
so52403065.py: error: argument --foo: invalid str_to_bool value: 'arrbuggrhellno'

【讨论】:

  • 所以我想要这个的原因是因为我正在编写的是一个构建器文件,它编写了许多 bash 脚本并将它们提交给 SLURM 工作负载管理器。我提交的可选参数会经过一些预处理步骤,然后将一组派生参数附加到 SLURM 脚本内的 python 调用中。现在,我发现实现这一点的最简单方法是拥有一个我想要传递给 SLURM 脚本并附加所有键值对的参数字典。现在根据您的建议,我必须检查是否手动设置了标志。
  • @Hyperplane:你可以接受 JSON 编码的配置字符串吗?那么如果args.json_config 是解码后的JSON 对象,那么你所要做的就是使用args = parser.parse_args()args.__dict__.update(args.json_config)
  • @Hyperplane: parser.add_argument('--json-config', type=json.loads) 应该足以定义这样一个开关。
  • 这听起来是一个非常好的解决方案。目前使用type=string2bool 似乎就足够了,但我会牢记这个想法。再次感谢。
  • 为 --no- 模式 argparse 引入了 BooleanOptionalAction docs.python.org/3/library/argparse.html#action
【解决方案2】:

您应该使用 action='store_true' 参数代替布尔参数:

parser.add_argument('--foo', action='store_true')

所以没有--foo选项:

python test.py

将导致foo 参数的False 值和--foo 选项的存在:

python test.py --foo

将导致foo 参数的True 值。

【讨论】:

  • 但这不允许--foo bool
  • 不需要。我已经用更好的解释更新了我的答案。
  • 当然,但是有个用例用于指定禁用 foo 选项,特别是在“来自包括命令行在内的多个来源的合并配置”中' 设想。 --no-foo 是那里的常用选择。
  • @blhsing 例如:我来到这里是因为我想知道如何使用 argparse 同时拥有 --foo--foo stuff
猜你喜欢
  • 2019-11-02
  • 2011-10-14
  • 2013-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多