首页 文章

使用argparse解析布尔值

提问于
浏览
393

我想使用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("") evalutates为 False .

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

13 回答

  • 1

    使用之前建议的另一种解决方案,但"correct"的"correct"解析错误:

    def str2bool(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=NICE,
                            help="Activate nice mode.")
    

    允许我使用:

    script --nice
    script --nice <bool>
    

    并仍然使用默认值(特定于用户设置) . 这种方法的一个(间接相关的)缺点是'nargs'可能会捕获位置参数 - 请参阅this related questionthis argparse bug report .

  • 18

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

    command --feature
    

    command --no-feature
    

    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)
    

    当然,如果你真的想要 --arg <True|False> 版本,你可以传递 ast.literal_eval 作为"type",或用户定义的函数......

    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?
    
  • 8

    我推荐mgilson的回答,但是有一个相互排斥的小组
    这样你就不能同时使用 --feature--no-feature .

    command --feature
    

    command --no-feature
    

    但不是

    command --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')
    
  • 20

    关于 type=booltype='bool' 可能意味着什么,似乎有些混乱 . 一个(或两个)是否应该'运行函数 bool() ,或'return a boolean'?现在 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() 未记录,但也未隐藏 . 在大多数情况下,程序员不需要知道它,因为 typeaction 获取函数和类值 . 有很多stackoverflow示例为两者定义自定义值 .


    如果从前面的讨论中看不出来, bool() 并不意味着'parse a string' . 从Python文档:

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

    与此形成鲜明对比

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

  • 13

    oneliner:

    parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
    
  • 17

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

    def str2bool(v):
      return v.lower() in ("yes", "true", "t", "1")
    

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

  • 2

    这是另一个没有额外行/ 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 == True:
         print("Do something")
    else:
         print("Don't do something")
    print("Check that args.do_something=" + str(args.do_something) + " is always a bool")
    
  • 5

    除了@mgilson所说的,还应该注意的是,还有一个ArgumentParser.add_mutually_exclusive_group(required=False)方法可以使 --flag--no-flag 同时不被使用变得微不足道 .

  • 1

    这适用于我期望的一切:

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

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

    parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
    
  • 122

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

    feature.add_argument('--feature',action='store_true')
    

    如果你在命令中设置参数--feature

    command --feature
    

    参数将为True,如果你没有设置类型--feature,参数默认值总是为假!

  • 150
    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)
    
  • 30

    我认为最规范的方式是:

    parser.add_argument('--ensure', nargs='*', default=None)
    
    ENSURE = config.ensure is None
    

相关问题