用argparse解析布尔值


610

我想使用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进行解析"False""F"以及它们的小写形式是False什么?


40
这是@mgilson答案的单线 解释parser.add_argument('--feature', dest='feature', default=False, action='store_true')。此解决方案将确保您始终获得bool带有True或值的类型False。(此解决方案有一个约束条件:您的选项必须具有默认值。)
Trevor Boyd Smith,

7
这是@Maxim答案 的单线解释parser.add_argument('--feature', dest='feature', type=lambda x:bool(distutils.util.strtobool(x)))。使用该选项时,此解决方案将确保bool值为True或的类型False。不使用该选项时,您将得到None。(distutils.util.strtobool(x)来自另一个stackoverflow问题
Trevor Boyd Smith,

8
怎么样parser.add_argument('--my_bool', action='store_true', default=False)
AruniRC '17

Answers:


272

另一个解决方案使用了先前的建议,但存在来自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>

并仍使用默认值(特定于用户设置)。这种方法的一个(间接相关的)缺点是“水罐”可能会引起位置争执-请参阅此相关问题此argparse错误报告


4
nargs ='?' 表示零或一个参数。docs.python.org/3/library/argparse.html#nargs
Maxim

1
我喜欢这个,但是我的default = NICE等效项给我一个错误,所以我必须做点其他事情。
Michael Mathews

2
@MarcelloRomani str2bool不是Python意义上的类型,它是上面定义的函数,您需要在某个地方包含它。
Maxim

4
的代码str2bool(v)可以替换为 bool(distutils.util.strtobool(v))。资料来源:stackoverflow.com/a/18472142/2436175
Antonio

4
也许值得一提的是,用这种方法您无法检查是否将参数设置为,if args.nice:因为如果将参数设置为False,它将永远不会通过条件。如果正确,那么最好从str2bool函数返回list 并将list设置为const参数,例如[True][False]。如果我错了,请纠正我
NutCracker

885

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

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作为“类型”或用户定义的函数来传递...

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?

96
我仍然认为type=bool应该开箱即用(考虑位置参数!)。即使另外指定choices=[False,True],您最终也会将“ False”和“ True”都视为True(由于从字符串转换为布尔值?)。 可能是相关问题
海豚

41
是的,我只是认为没有理由不能按预期进行。这是极具误导性的,因为没有安全检查,也没有错误消息。
2013年

69
@mgilson-我发现误导性在于您可以设置type = bool,没有错误消息,但是,对于“ False”和“ True”字符串参数,您都应该在布尔变量中得到True(由于类型转换适用于python)。因此,显然不应该支持type = bool(发出一些警告,错误等),或者应该以有用且直观地预期的方式工作。
海豚

14
@dolphin-我不同意。我认为该行为完全是应有的方式,并且与zen python一致:“特殊情况不足以违反规则”。但是,如果您对此感到强烈,为什么不将其显示在各种python 邮件列表之一中呢?在那里,你可能会在说服别人谁拥有权力的机会一些有关这个问题。即使您能够说服我,您也只能说服我,而且由于我不是开发人员,因此行为仍然不会改变:)
mgilson 2013年

15
我们是在争论Python bool()函数应该做什么,还是argparse应该接受type=fn什么?所有argparse检查都是fn可以调用的。它期望fn采用一个字符串参数,并返回一个值。的行为fn不是程序员的责任,而是argparse's
hpaulj

235

我建议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')

5
@CharlieParker add_argument被调用dest='feature'set_defaults被称为feature=True。了解?
fnkr

4
这个答案或mgilson的答案应该是被接受的答案-即使OP想要--flag False,SO答案的一部分也应该与他们试图解决的问题有关,而不仅仅是与HOW有关。绝对没有理由这样做,--flag False或者--other-flag True使用一些自定义解析器将字符串转换为布尔值.. action='store_true'以及action='store_false'使用布尔值标志的最佳方法
kevlarr

6
@cowlinator为什么最终要回答“陈述的问题”呢?根据其自身的指导原则... can be “don’t do that”, but it should also include “try this instead”(至少对我而言)暗示答案的答案应在适当时加深。当然,有时候我们中的一些人可以从更好/最佳实践等方面的指导中受益。回答“如上所述”通常并不能做到这一点。话虽这么说,但您对答案的无奈通常会假设太多(或错误地)是完全正确的。
kevlarr '18

2
如果要在用户未明确指定功能时使用第三个值,则需要用parser.set_defaults(feature=None)
Alex Che

2
如果我们要help=为此参数添加一个条目,应该放在哪里?在add_mutually_exclusive_group()通话中?在一个或两个add_argument()电话中?别的地方?
肯·威廉姆斯

57

这是另一种变体,无需额​​外的行来设置默认值。布尔值始终分配有一个值,以便可以在逻辑语句中使用它而无需预先检查。

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

5
这个答案被低估了,但是简单易用。不要尝试设置required=True,否则您将始终获得True arg。
Garren S '18年

1
不要在bool或nonetype之类的东西上使用相等运算符。您应该使用IS 代替
webKnjaZ

2
这是比接受的更好的答案,因为它仅检查设置布尔值的标志的存在,而不需要冗余的布尔字符串。(Y,我听说你喜欢布尔值,所以我给了你一个布尔值来设置布尔值!)
虹吸

4
嗯...如上所述,这个问题似乎想在命令行本身上使用“ True” /“ False”。但是,在此示例中,python3 test.py --do-something False失败了error: unrecognized arguments: False,因此并不能真正回答问题。
sdbbs

38

单线:

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

4
对单线风扇很好,也可以进行一些改进:type=lambda x: (str(x).lower() in ['true','1', 'yes'])
Tu Bui

35

关于什么type=bool以及type='bool'可能意味着什么似乎有些困惑。一个(或两个)是否应该表示“运行函数bool()或”返回布尔值”?就目前而言,它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()不出来,就不意味着“解析字符串”。从Python文档中:

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

与之对比

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


3
或使用:parser.register('type','bool',(lambda x:x.lower()in(“ yes”,“ true”,“ t”,“ 1”))))
Matyas,2016年

17

我一直在寻找相同的问题,恕我直言,漂亮的解决方案是:

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

并按照上面的建议使用它来将字符串解析为布尔值。


5
如果您要走这条路,我建议distutils.util.strtobool(v)
CivFan

1
所述distutils.util.strtobool返回1或0,而不是实际的布尔值。
CMCDragonkai

14

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

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

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

 command --feature

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


1
此方法是否有其他答案可以克服的缺点?到目前为止,这似乎是最简单,最简洁的解决方案,可以满足OP(在本例中为我)的需求。我喜欢它。
西蒙·奥汉隆

2
虽然简单,但是它不能回答问题。OP想要一个可以在其中指定的参数--feature False
Astariul


11

这适用于我期望的所有功能:

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

优秀的!我要这个答案。我调整了一次_str_to_bool(s)转换s = s.lower(),然后进行测试if s not in {'true', 'false', '1', '0'},最后进行调整return s in {'true', '1'}
Jerry101 '18

6

一种更简单的方法是按以下方式使用。

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

5

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

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

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


这看起来确实很方便。我注意到您有eval作为类型。我对此有一个疑问:应该如何定义eval,或者需要导入才能使用它?
edesz

1
eval是内置函数。docs.python.org/3/library/functions.html#eval这可以是任何一元函数,其他更灵活的方法也可以利用。
罗素

嘿,太好了。谢谢!
edesz

1
这很可爱,但是冒险放任不管,那些不知道eval是邪恶的用户将其复制粘贴到脚本中是非常危险的。
Arne

@Arne,好点。虽然,对于一个好主意的用户来说,意外地做一些有害的事情似乎非常困难。
罗素

3

最简单的方法是使用选择

parser = argparse.ArgumentParser()
parser.add_argument('--my-flag',choices=('True','False'))

args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)

不通过--my-flag评估为False。该要求=真,如果你总是希望用户显式地指定一个选择的选项可以添加。



1
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

最简单,最正确的方法是

from distutils import util
arser.add_argument('--feature', dest='feature', type=lambda x:bool(distutils.util.strtobool(x)))

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


0

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

-1

类似于@Akash,但这是我使用的另一种方法。它之所以有用strlambda是因为python lambda总是给我一种外星人的感觉。

import argparse
from distutils.util import strtobool

parser = argparse.ArgumentParser()
parser.add_argument("--my_bool", type=str, default="False")
args = parser.parse_args()

if bool(strtobool(args.my_bool)) is True:
    print("OK")

-1

为了改善@Akash Desarda的答案,您可以做

import argparse
from distutils.util import strtobool

parser = argparse.ArgumentParser()
parser.add_argument("--foo", 
    type=lambda x:bool(strtobool(x)),
    nargs='?', const=True, default=False)
args = parser.parse_args()
print(args.foo)

它支持 python test.py --foo

(base) [costa@costa-pc code]$ python test.py
False
(base) [costa@costa-pc code]$ python test.py --foo 
True
(base) [costa@costa-pc code]$ python test.py --foo True
True
(base) [costa@costa-pc code]$ python test.py --foo False
False
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.