使用argparse解析布尔值

Sup*_*ric 532 python boolean command-line-parsing argparse

我想使用argparse来解析写为"--foo True"或"--foo False"的布尔命令行参数.例如:

my_program --my_boolean_flag False
Run Code Online (Sandbox Code Playgroud)

但是,以下测试代码不能满足我的要求:

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)
Run Code Online (Sandbox Code Playgroud)

可悲的是,parsed_args.my_bool评估为True.这种情况即使我改变cmd_line["--my_bool", ""],这是令人惊讶的,因为bool("")重新评估False.

如何让argparse解析"False","F"以及它们的小写变体False

mgi*_*son 778

我认为一个更规范的方法是通过:

command --feature
Run Code Online (Sandbox Code Playgroud)

command --no-feature
Run Code Online (Sandbox Code Playgroud)

argparse 很好地支持这个版本:

parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
Run Code Online (Sandbox Code Playgroud)

当然,如果你真的想要--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?
Run Code Online (Sandbox Code Playgroud)

  • 我仍然认为`type = bool`应该开箱即用(考虑位置参数!).即使你另外指定`choices = [False,True]`,你最终都会将"False"和"True"都视为True(由于从字符串转换为bool?).[也许相关问题](http://bugs.python.org/issue14392) (91认同)
  • @mgilson - 我发现误导的是你**可以**设置type = bool,你没有得到任何错误信息,然而,对于"False"和"True"字符串参数,你在你所谓的布尔变量中得到True (由于类型转换如何在python中工作).因此,要么显然不支持type = bool(发出一些警告,错误等),要么它应该以一种有用且直观的方式工作. (61认同)
  • 是的,我认为这没有理由不按预期工作.这是非常误导的,因为没有安全检查也没有错误信息. (41认同)
  • @dolphin - 我分别不同意.我认为行为正是应该的样子,并且与python的禅宗一致"特殊情况不足以打破规则".但是,如果您对此有强烈的感受,为什么不在各种python [邮件列表](https://mail.python.org/mailman/listinfo)上提出它呢?在那里,你可能有机会说服那些有能力做某事的人.即使你能够说服我,你也只能成功说服我,并且由于我不是开发人员,行为仍然不会改变:) (14认同)
  • 我们是在讨论Python`bool()`函数应该做什么,或者argparse应该在`type = fn`中接受什么?所有`argparse`检查都是`fn`是可调用的.它希望`fn`接受一个字符串参数,并返回一个值.`fn`的行为是程序员的责任,而不是'argparse'. (14认同)
  • @ChristopheRoussy - 我仍然认为这是最好的行为......你正在寻找python做魔法.(基于这个答案的流行,很多人可能都是).然而,魔术的问题在于每个人都希望它表现得与众不同.你接受什么?"真","假"?怎么样'真实'?怎么样'是'/'不是'?西班牙语的"Si"怎么样?弄清楚在哪里绘制线条变得困难.我认为在这种情况下,最好做最小化并强制用户明确. (6认同)
  • @dolphin - 你的意思是"因为这不能按预期工作"?正如我所看到的,这完全符合预期.`type`参数对命令行输入的字符串进行操作以构造输出.你指的是误导性的是什么?我的答案中有什么可以改进的吗?如果是这样,请具体告诉我,我会尽力澄清.最终,argparse是结构化的,因为大多数程序没有像`-arg True | False`这样的标志.OP在这里寻找的是非标准的,这就是为什么它不是由`argparse`提供的. (4认同)
  • @mgilson嗯,有各种Python规则,无论你应用哪一个,当前 type=bool 参数的解决方案都是无用且具有误导性的(或者我是否错过了 type=bool 当前行为的任何使用?)它不是也不一致吗例如 int args 的行为?(我猜“0”为 0,“5”为 5)。我不认为它是最优先的问题,只是觉得当前的 bool 解决方案并不那么有用,并且对于某些程序员来说可能会导致他们的应用程序出现意外的结果。 (4认同)
  • 对这个答案的评论是荒谬的.为什么有人会期望`bool('我的非空的,真实的字符串')`返回`False`?如果你不愿意,为什么期望`bool('False')`返回'True`?你不明白传递给`type =`的参数只是一个函数(或任何可调用的,真的)?这就是为什么`int`有效 - 因为`int('5')`返回一个整数.`str('foo')`有效,因为`str`返回一个字符串.这有什么争议?也许`type`对于关键字参数来说是一个坏名字 - 也许`parse_func`或某些东西会让它更明显它不是魔术? (4认同)
  • @dolphin - 您可以使用`type = {'True':True,'False':False} .get`但请记住,`bool('False')`是'True`,所以传递`bool`作为类型构造函数在这里不起作用. (3认同)
  • 这种行为肯定是误导性的:它适用于str,适用于int,但是bool失败了吗? (3认同)
  • @mgilson我希望它在帮助'真或假(任何情况)'中显示允许值,或者它应该失败并拒绝bool.我对python很新,所以这就是我期望的一个工具,它应该处理标准的情况,比如str,int,bool,......我知道它有点神奇,因为案件处理...... (2认同)
  • 虽然 --no-feature 版本很好,但支持 --feature=False 在没有指定参数的情况下工作具有非常有效的用例:当客户端准备标志的布尔值并说程序被调用时--功能=$VALUE。 (2认同)
  • 布尔标志是一种极其常见的用例,因此单行解决方案对我来说似乎非常理想。IMO 也没有理由让用户期望设置 `type=bool` 会导致 *parser* 成为 `bool` 函数。(事实上​​,似乎不太可能存在用于制作将“随机字符串”解释为“True”的布尔标志的用例......?)我会考虑在未来尝试为改进这一点做出贡献!:) (2认同)
  • 我不明白为什么你必须为布尔值的两边进行争论。为非默认情况添加参数。默认为默认情况。完毕。`parser.add_argument('-n', '--no-feature', dest='nofeature', default=False, action='store_true'); ...; feature_bool = not parser.parse_args().nofeature` 为什么要让用户以默认大小写输入? (2认同)
  • @HelloGoodbye 如果您使用 type=float 并提供“3.14”(字符串),它将转换为 3.14。如果您使用 type=bool 并提供“False”(字符串),它将转换为 True。很多人会将此称为不一致、意外的误导行为,因为 float 和 bool 是 Python 中的数据类型。 (2认同)
  • @HelloGoodbye 我在各种编程语言方面拥有 30 年的经验,从机器代码和汇编到 Python,所以我非常理解其中的原因。只是指出不一致以及误导行为和隐藏/无声错误的可能性。还指出,如果它“按预期”工作,那么它将对很多人有用。 (2认同)
  • @HelloGoodbye,因为您指示了您想要的类型(bool),所以我相信这是显而易见的。当你声明一个参数类型为int时,你是否会问“哪些字符串应该转换为-10”、“哪些字符串应该转换为0”、“哪些字符串应该转换为52”? (2认同)
  • @HelloGoodbye 转换应该像 int 或 float 一样工作......您将“0”转换为 0,将“5.23”转换为 5.23。类似地,“真”对真,“假”对假。“x”会导致异常,因为表明他们需要一个 bool,就像“x”在表明他们需要一个 int 或 float 时会导致异常一样。 (2认同)
  • @dolphin另外,当你考虑一下时,“str(False)”变成“False”,但“bool('False')”变成“True”,并没有多大意义。所以,是的,Python 在这方面并不是非常一致。 (2认同)

Max*_*xim 218

使用之前建议的另一种解决方案,但具有"正确"解析错误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.')
Run Code Online (Sandbox Code Playgroud)

这对于使用默认值制作开关非常有用; 例如

parser.add_argument("--nice", type=str2bool, nargs='?',
                        const=True, default=False,
                        help="Activate nice mode.")
Run Code Online (Sandbox Code Playgroud)

允许我使用:

script --nice
script --nice <bool>
Run Code Online (Sandbox Code Playgroud)

并仍然使用默认值(特定于用户设置).这种方法的一个(间接相关的)缺点是'nargs'可能会捕获位置参数 - 请参阅此相关问题这个argparse错误报告.

  • `str2bool(v)` 的代码可以替换为 `bool(distutils.util.strtobool(v))`。来源:/sf/answers/1293049971/ (13认同)
  • nargs='?' 表示零个或一个参数。https://docs.python.org/3/library/argparse.html#nargs (6认同)
  • @MarcelloRomani str2bool不是Python意义上的类型,它是上面定义的函数,你需要将它包含在某个地方. (2认同)
  • 也许值得一提的是,用这种方法您不能检查是否用`if args.nice:`设置了参数,因为如果将参数设置为False,它将永远不会通过条件。如果这是正确的,那么最好从str2bool函数返回list并将list设置为const参数,例如this [True],`[False]。如果我错了请纠正我 (2认同)

fnk*_*nkr 215

我建议mgilson的答案,但有互相排斥的群体
,这样就可以不使用--feature,并--no-feature在同一时间.

command --feature
Run Code Online (Sandbox Code Playgroud)

command --no-feature
Run Code Online (Sandbox Code Playgroud)

但不是

command --feature --no-feature
Run Code Online (Sandbox Code Playgroud)

脚本:

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)
Run Code Online (Sandbox Code Playgroud)

如果要设置其中许多帮助,则可以使用此帮助程序:

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')
Run Code Online (Sandbox Code Playgroud)

  • @cowlinator为什么SO最终要回答"陈述的问题"?根据[自己的指南](https://stackoverflow.com/help/how-to-answer),anwer`...可以"不要那样做",但它也应该包括"试试这个"(至少对我而言)暗示答案应该在适当的时候更深入.肯定有些时候,我们中的一些人发布问题可以从更好/最佳实践的指导等方面受益.回答"如上所述"通常不会这样做.话虽这么说,你对答案的沮丧经常假设太多(或不正确)是完全有效的. (6认同)
  • 使用`dest ='feature'`调用@CharlieParker`add_argument`.使用`feature = True`调用`set_defaults`.了解? (5认同)
  • 这个或者mgilson的答案应该是公认的答案 - 即使OP想要` - 标志错误',SO答案的一部分应该是关于他们试图解决的问题,而不仅仅是如何解决.应该绝对没有理由做`--flag False`或`--other-flag True`然后使用一些自定义解析器将字符串转换为布尔值.`action ='store_true'`和`action =' store_false'`是使用布尔标志的最佳方法 (4认同)
  • set_default 如何知道在哪里保存默认值? (2认同)
  • 如果要在用户未明确指定功能时使用第三个值,则需要用parser.set_defaults(feature = None)替换最后一行。 (2认同)
  • 如果我们想为该参数添加一个“ help =”条目,应该放在哪里?在“ add_mutually_exclusive_group()”调用中?在一个或两个add_argument()调用中?别的地方? (2认同)

Sch*_*aki 34

这是另一个没有额外行/ s来设置默认值的变体.bool始终具有一个赋值,以便可以在没有预先检查的逻辑语句中使用它.

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("Check that args.do_something=" + str(args.do_something) + " is always a bool")
Run Code Online (Sandbox Code Playgroud)

  • 嗯...正如所述,问题似乎想在命令行本身上使用“True”/“False”;然而在这个例子中,“python3 test.py --do-something False”失败并显示“error: unrecognized argument: False”,所以它并没有真正回答问题。 (7认同)
  • 这是一个比接受的更好的答案,因为它只是检查标志的存在来设置布尔值,而不是需要冗余的布尔字符串。(哟,dawg,我听说你喜欢布尔值......所以我给了你一个布尔值和你的布尔值来设置你的布尔值!) (6认同)
  • 这个答案被低估了,但是简单易用。不要尝试设置`required = True`,否则您将始终得到True arg。 (2认同)

hpa*_*ulj 33

关于什么type=booltype='bool'可能意味着似乎有些混乱.一个(或两个)是否意味着'运行函数bool(),或'返回一个布尔值'?因为它type='bool'没有任何意义. add_argument给出'bool' is not callable错误,就像你使用的一样type='foobar',或者type='int'.

但是argparse有注册表可以让你定义像这样的关键字.它主要用于action,例如`action ='store_true'.您可以看到已注册的关键字:

parser._registries
Run Code Online (Sandbox Code Playgroud)

它显示一本字典

{'action': {None: argparse._StoreAction,
  'append': argparse._AppendAction,
  'append_const': argparse._AppendConstAction,
...
 'type': {None: <function argparse.identity>}}
Run Code Online (Sandbox Code Playgroud)

定义了许多操作,但只有一种类型,默认类型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)
Run Code Online (Sandbox Code Playgroud)

parser.register()没有记录,但也没有隐藏.在大多数情况下,程序员并不需要了解它,因为typeaction采取函数和类值.有很多stackoverflow示例为两者定义自定义值.


如果从前面的讨论中不明显,bool()并不意味着'解析字符串'.从Python文档:

bool(x):使用标准真值测试程序将值转换为布尔值.

与此形成鲜明对比

int(x):将数字或字符串x转换为整数.

  • 或者使用:parser.register('type','bool',(lambda x:x.lower()in("yes","true","t","1"))) (3认同)

Eva*_*ans 31

oneliner:

parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
Run Code Online (Sandbox Code Playgroud)

  • 另一种选择是使用标准 `distutils.utils.strtobool`,例如 `type=lambda x: bool(strtobool(str(x)))` 。真实值为 y、yes、t、true、on 和 1;假值为 n、no、f、false、off 和 0。 (4认同)
  • 对单线风扇好,也可以改进:`type = lambda x:([[true],'1','yes']中的str(x).lower()) (3认同)
  • Small correction @Jethro's comment: This should be `distutils.util.strtobool` (no `s`). Works great! (2认同)

sus*_*erg 18

我一直在寻找相同的问题,而且这个漂亮的解决方案是:

def str2bool(v):
  return v.lower() in ("yes", "true", "t", "1")
Run Code Online (Sandbox Code Playgroud)

并使用它将字符串解析为boolean,如上所述.

  • 如果你要走这条路,我可以建议`distutils.util.strtobool(v)`. (5认同)
  • `distutils.util.strtobool` 返回 1 或 0,而不是实际的布尔值。 (2认同)

小智 15

最简单和最正确的方法是:

from distutils.util import strtobool

parser.add_argument('--feature', dest='feature', 
                    type=lambda x: bool(strtobool(x)))
Run Code Online (Sandbox Code Playgroud)

请注意,True 值是 y、yes、t、true、on 和 1;false 值为 n、no、f、false、off 和 0。如果 val 是其他值,则引发 ValueError。


foo*_*foo 13

除了@mgilson所说的,还应该注意的是,还有一种ArgumentParser.add_mutually_exclusive_group(required=False)方法可以使得强制执行该方法--flag并且--no-flag不会同时使用.


dl.*_*teo 12

一个非常类似的方法是使用:

feature.add_argument('--feature',action='store_true')
Run Code Online (Sandbox Code Playgroud)

并且如果您在命令中设置参数--feature

 command --feature
Run Code Online (Sandbox Code Playgroud)

如果未设置type --feature,则参数将为True,参数默认始终为False!

  • 虽然简单,但它并不能回答问题。OP想要一个参数,您可以在其中指定“--feature False” (4认同)

Rus*_*ell 11

最简单。它不灵活,但我更喜欢简单。

  parser.add_argument('--boolean_flag',
                      help='This is a boolean flag.',
                      type=eval, 
                      choices=[True, False], 
                      default='True')
Run Code Online (Sandbox Code Playgroud)

编辑:如果您不信任输入,请不要使用eval.

  • 这很可爱,但放在野外是相当危险的,那些不知道[eval是邪恶的](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html)的用户只会将其复制粘贴到他们的脚本。 (3认同)

Zac*_*eck 11

这实际上已经过时了。对于 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()
Run Code Online (Sandbox Code Playgroud)

另一件事要提的是:这将通过 argparse.ArgumentTypeError 阻止参数除 True 和 False 之外的所有条目。如果您出于任何原因想要尝试更改此设置,您可以为此创建一个自定义错误类。

  • **不**。这不是 `action=argparse.BooleanOptionalAction` 的工作原理。之前在 `type` 下,它警告不要使用 `type=bool`,`"不建议将 bool() 函数作为类型转换器。它所做的只是将空字符串转换为 False,将非空字符串转换为 True。这通常不是所期望的。”`。 (9认同)
  • 同意,这个答案不应该被接受: `args = ap.parse_args(['--foo', 'False'])` 返回 True (在我看来,它不应该 (8认同)

小智 8

这适用于我期望的一切:

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
Run Code Online (Sandbox Code Playgroud)

代码:

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')
Run Code Online (Sandbox Code Playgroud)


小智 6

更简单的方法是使用如下.

parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
Run Code Online (Sandbox Code Playgroud)


Ind*_*jar 6

在之前遵循@akash-desarda的卓越答案/sf/answers/4170581341/之后,使用strtoboolvia ,后来,我决定直接lambda使用。strtobool

import argparse
from distutils import util
parser.add_argument('--feature', type=util.strtobool)
Run Code Online (Sandbox Code Playgroud)

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

>>> 0 == False
True
>>> 0 == True
False
>>> 1 == False
False
>>> 1 == True
True
Run Code Online (Sandbox Code Playgroud)

当收到错误的输入值时,例如

python yours.py --feature wrong_value
Run Code Online (Sandbox Code Playgroud)

与 argparse.Actionstrtobool进行比较lambda将产生稍微更清晰/易于理解的错误消息:

yours.py: error: argument --feature: invalid strtobool value: 'wrong_value'
Run Code Online (Sandbox Code Playgroud)

与这段代码相比,

parser.add_argument('--feature', type=lambda x: bool(util.strtobool(x))
Run Code Online (Sandbox Code Playgroud)

这将产生不太清晰的错误消息:

yours.py: error: argument --feature: invalid <lambda> value: 'wrong_value'
Run Code Online (Sandbox Code Playgroud)