Parsing boolean values with argparse
我想使用argparse解析写为"-foo-true"或"-foo-false"的布尔命令行参数。例如:
1 | my_program --my_boolean_flag False |
但是,以下测试代码不符合我的要求:
1 2 3 4 5 | 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) |
遗憾的是,
如何让argparse解析
我认为更规范的方法是通过:
1 | command --feature |
和
1 | command --no-feature |
1 2 3 | parser.add_argument('--feature', dest='feature', action='store_true') parser.add_argument('--no-feature', dest='feature', action='store_false') parser.set_defaults(feature=True) |
当然,如果你真的想要
1 2 3 4 5 6 7 8 | 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? |
我推荐米吉尔森的答案,但有一个相互排斥的小组所以你不能同时使用
1 | command --feature |
和
1 | command --no-feature |
但不是
1 | command --feature --no-feature |
脚本:
1 2 3 4 | 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) |
然后,如果要设置其中许多帮助器,则可以使用此帮助器:
1 2 3 4 5 6 7 8 | 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') |
另一种解决方案是使用前面的建议,但使用
1 2 3 4 5 6 7 | 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.') |
这对于使用默认值进行切换非常有用;例如
1 2 3 | parser.add_argument("--nice", type=str2bool, nargs='?', const=True, default=NICE, help="Activate nice mode.") |
允许我使用:
1 2 | script --nice script --nice <bool> |
仍然使用默认值(特定于用户设置)。这种方法的一个(间接相关的)缺点是"nargs"可能捕捉到位置参数——请参阅这个相关问题和argparse bug报告。
关于
但是,
1 | parser._registries |
显示字典
1 2 3 4 5 | {'action': {None: argparse._StoreAction, 'append': argparse._AppendAction, 'append_const': argparse._AppendConstAction, ... 'type': {None: <function argparse.identity>}} |
定义了很多操作,但只有一种类型,默认类型,
此代码定义了一个"bool"关键字:
1 2 3 4 5 6 7 8 9 | 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) |
如果前面的讨论不明显,那么
bool(x): Convert a value to a Boolean, using the standard truth testing procedure.
把这个和
int(x): Convert a number or string x to an integer.
oneliner:
1 | parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true')) |
这里是另一个没有额外行/秒来设置默认值的变体。bool总是分配一个值,这样就可以在没有预检查的情况下在逻辑语句中使用它。
1 2 3 4 5 6 7 8 9 10 | 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") |
我也在寻找同样的问题,我想最好的解决办法是:
1 2 | def str2bool(v): return v.lower() in ("yes","true","t","1") |
并使用它将字符串解析为布尔值,如上所述。
除了@mgilson所说的,还应该注意到还有一个
这适用于我所期望的一切:
1 2 3 4 5 6 7 | 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 |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 | 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') |
类似的方法是使用:
1 | feature.add_argument('--feature',action='store_true') |
如果您设置了参数——命令中的特性
1 | command --feature |
如果不设置类型,参数将为true——特性参数默认值始终为false!
一个简单的方法是如下使用。
1 | parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1']) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) |
我认为最典型的方法是:
1 2 3 | parser.add_argument('--ensure', nargs='*', default=None) ENSURE = config.ensure is None |