ChatGPT解决这个技术问题 Extra ChatGPT

使用 argparse 解析布尔值

我想使用 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

这是对 @mgilson's answer parser.add_argument('--feature', dest='feature', default=False, action='store_true') 的单行解释。此解决方案将确保您始终获得值为 TrueFalsebool 类型。 (此解决方案有一个约束:您的选项必须具有默认值。)
这是对 @Maxim's answer parser.add_argument('--feature', dest='feature', type=lambda x:bool(distutils.util.strtobool(x))) 的单行解释。使用该选项时,此解决方案将确保 bool 类型的值为 TrueFalse。如果不使用该选项,您将获得 None。 (distutils.util.strtobool(x) is from another stackoverflow question)
parser.add_argument('--my_bool', action='store_true', default=False)这样的东西怎么样
对于@TrevorBoydSmith 的回答,请尝试使用 import distutils.util 而不是 import disutils 导入。请参阅this answer
刚刚遇到同样的问题。令人震惊的是 argparse 模块有多么不必要的大和杂草丛生,但它仍然没有做它应该开箱即用的简单事情。更糟糕的是,它做错了。

T
Tahlor

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

command --feature

command --no-feature

argparse 很好地支持这个版本:

Python 3.9+

parser.add_argument('--feature', action=argparse.BooleanOptionalAction)

Python < 3.9:

parser.add_argument('--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?

我仍然认为 type=bool 应该开箱即用(考虑位置参数!)。即使您另外指定 choices=[False,True],您最终也会将“False”和“True”都视为 True(由于从字符串转换为布尔值?)。 Maybe related issue
是的,我只是认为没有理由不按预期工作。这是极具误导性的,因为没有安全检查或错误消息。
@mgilson - 我发现误导的是你可以设置 type=bool,你不会收到错误消息,但是,对于“False”和“True”字符串参数,你在你所谓的布尔变量中得到 True(由于类型转换在 python 中工作)。因此,要么 type=bool 显然不被支持(发出一些警告、错误等),要么它应该以一种有用且直观预期的方式工作。
@dolphin——我不同意。我认为这种行为正是它应有的方式,并且符合 python 的禅宗“特殊情况不足以打破规则”。但是,如果您对此有强烈的感觉,为什么不在各种 python mailing lists 之一上提出呢?在那里,您可能有机会说服有权在此问题上某事的人。即使您能够说服我,您也只会成功地说服我,并且行为仍然不会改变,因为我不是开发人员:)
我们是否在争论 Python bool() 函数应该做什么,或者 argparse 在 type=fn 中应该接受什么?所有 argparse 检查是 fn 是可调用的。它期望 fn 接受一个字符串参数,并返回一个值。 fn 的行为是程序员的责任,而不是 argparse's
d
dennlinger

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

并且仍然使用默认值(特定于用户设置)。这种方法的一个(间接相关的)缺点是“nargs”可能会捕捉到位置参数——参见this related questionthis argparse bug report


nargs='?'表示零个或一个参数。 docs.python.org/3/library/argparse.html#nargs
我喜欢这个,但我相当于 default=NICE 给了我一个错误,所以我必须做点别的。
@MarcelloRomani str2bool 不是 Python 意义上的类型,它是上面定义的函数,您需要将它包含在某个地方。
str2bool(v) 的代码可以替换为 bool(distutils.util.strtobool(v))。来源:stackoverflow.com/a/18472142/2436175
也许值得一提的是,通过这种方式,您无法检查参数是否设置为 if args.nice:,因为如果参数设置为 False,它将永远不会通过条件。如果这是正确的,那么最好从 str2bool 函数返回列表并将列表设置为 const 参数,例如 [True][False]。如果我错了,请纠正我
f
fnkr

如果您想同时允许 --feature 和 --no-feature (最后一个获胜)

这允许用户使用 --feature 创建 shell 别名,并使用 --no-feature 覆盖它。

Python 3.9 及更高版本

parser.add_argument('--feature', default=True, action=argparse.BooleanOptionalAction)

Python 3.8 及以下

我推荐mgilson的回答:

parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)

如果您不想同时允许 --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')

@CharlieParker add_argumentdest='feature' 调用。 set_defaultsfeature=True 一起调用。理解?
这个或 mgilson 的答案应该是公认的答案 - 即使 OP 想要 --flag False,部分 SO 答案应该是关于他们试图解决的问题,而不仅仅是关于如何。绝对没有理由做 --flag False--other-flag True 然后使用一些自定义解析器将字符串转换为布尔值。action='store_true'action='store_false' 是使用布尔标志的最佳方法
@cowlinator 为什么最终要回答“所述问题”?根据its own guidelines,anwer ... can be “don’t do that”, but it should also include “try this instead”(至少对我而言)意味着答案应该在适当的时候更深入。有时我们中的一些人发布问题可以从更好/最佳实践等方面的指导中受益。回答“如前所述”通常不会做到这一点。话虽如此,您对经常假设太多(或错误)的答案感到沮丧是完全有效的。
如果想要在用户没有明确指定功能时获得第三个值,他需要用 parser.set_defaults(feature=None) 替换最后一行
如果我们想为这个参数添加一个 help= 条目,它应该放在哪里?在 add_mutually_exclusive_group() 通话中?在一个或两个 add_argument() 调用中?别的地方?
G
Gringo Suave

这是另一个没有额外行来设置默认值的变体。布尔值总是被分配的,因此它可以在逻辑语句中使用而无需事先检查:

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(f"Check that args.do_something={args.do_something} is always a bool.")

这个答案被低估了,但它的简单却很棒。不要尝试设置 required=True 否则您将始终获得 True arg。
请永远不要在 bool 或 nonetype 之类的东西上使用相等运算符。您应该改用 IS
这是一个比接受的更好的答案,因为它只是检查标志的存在以设置布尔值,而不是需要冗余的布尔字符串。 (Yo dawg,我听说你喜欢布尔值......所以我给你一个布尔值和你的布尔值来设置你的布尔值!)
嗯...如前所述,问题似乎想在命令行本身上使用“True”/“False”;但是,在此示例中,python3 test.py --do-something Falseerror: unrecognized arguments: False 失败,因此它并没有真正回答问题。
一个小提示:默认的 None 通常在这里也可以正常工作。
E
Evalds Urtans

单线:

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

对 oneliner 粉丝有好处,也可以改进一下:type=lambda x: (str(x).lower() in ['true','1', 'yes'])
另一种选择是使用标准的 distutils.utils.strtobool,例如 type=lambda x: bool(strtobool(str(x))) 。真值为 y、yes、t、true、on 和 1; false 值为 n、no、f、false、off 和 0。
h
hpaulj

对于 type=booltype='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

此代码定义了一个“布尔”关键字:

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 转换为整数。


或者使用:parser.register('type', 'bool', (lambda x: x.lower() in ("yes", "true", "t", "1")))
d
dl.meteo

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

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

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

 command --feature

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


这种方法是否有其他答案克服的一些缺点?这似乎是迄今为止最简单、最简洁的解决方案,可以满足 OP(在这种情况下是我)想要的。我喜欢它。
虽然简单,但它并没有回答这个问题。 OP 想要一个参数,您可以在其中指定 --feature False
G
Gringo Suave

最简单最正确的方法是:

from distutils.util import strtobool

parser.add_argument('--feature', dest='feature', 
                    type=lambda x: bool(strtobool(x)))

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


这个应该高很多! :-)
s
susundberg

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

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

并使用它来将字符串解析为布尔值,如上所述。


如果您打算走这条路,我建议您distutils.util.strtobool(v)
distutils.util.strtobool 返回 1 或 0,而不是实际的布尔值。
S
Stumpy Joe Pete

这适用于我期望的一切:

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'}
f
foo

除了@mgilson 所说的之外,还应该注意,还有一个 ArgumentParser.add_mutually_exclusive_group(required=False) 方法可以轻松强制执行 --flag--no-flag 不同时使用。


R
Russell

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

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

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


这看起来确实很方便。我注意到你有 eval 作为类型。我对此有一个问题:应该如何定义 eval ,或者是否需要导入才能使用它?
eval 是一个内置函数。 docs.python.org/3/library/functions.html#eval 这可以是其他更灵活的方法可以利用的任何一元函数。
这很可爱,但是如果不知道 eval being evil 的用户只会将其复制粘贴到他们的脚本中,那么将其放到野外是很冒险的。
@Arne,好点。尽管如此,一个好心的用户似乎很难不小心做一些有害的事情。
不使用。不仅不安全,最重要的答案也更加惯用。如果您仍然想走这条路,已经提到了一个流行的答案:ast.literal_eval更安全。
Z
Zach Rieck

这实际上已经过时了。对于 Python 3.7+,Argparse now supports boolean args(搜索 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()

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


您在 3.7+ 和 3.9+ 中提到了这一点。哪一个?
3.7+。在编辑中澄清
python 文档提到 New in version 3.9,我无法在 3.7 中从 argparse 导入 BooleanOptionalAction...
没有。这不是 action=argparse.BooleanOptionalAction 的工作方式。之前在 type 下,它警告不要使用 type=bool"The bool() function is not recommended as a type converter. All it does is convert empty strings to False and non-empty strings to True. This is usually not what is desired."
同意,这个答案不应该被接受:args = ap.parse_args(['--foo', 'False']) 返回 True (在我看来它不应该
P
Peter Mortensen

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

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

I
Indra Ginanjar

在之前遵循 @akash-desarda 的卓越答案 https://stackoverflow.com/a/59579733/315112 后,通过 lambda 使用 strtobool,后来,我决定直接使用 strtobool

import argparse
from distutils import util
parser.add_argument('--feature', type=util.strtobool)

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

>>> 0 == False
True
>>> 0 == True
False
>>> 1 == False
False
>>> 1 == True
True

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

python yours.py --feature wrong_value

lambda 相比,带有 strtobool 的 argparse.Action 将产生更清晰/易于理解的错误消息:

yours.py: error: argument --feature: invalid strtobool value: 'wrong_value'

与这段代码相比,

parser.add_argument('--feature', type=lambda x: bool(util.strtobool(x))

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

yours.py: error: argument --feature: invalid <lambda> value: 'wrong_value'

g
gerardw

最简单的方法是使用选择:

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。如果您总是希望用户明确指定一个选项,则可以添加 required=True 选项。


C
Costa Huang

作为对@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

P
Peter Mortensen

我认为最规范的方式是:

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

ENSURE = config.ensure is None

F
FEMengineer

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

这是最好的方法,0 和 1 很容易解释为 False 和 True。但是,您应该更正您的第一条语句,说 0 将返回 false,而 任何其他值 将返回 True。如果您确实想限制为 0,1,请添加如下选项:parser.add_argument("mybool", default=True,type=lambda x: bool(int(x)), choices=['0','1'])
R
Robert T. McGibbon
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)

Y
Youngjae

类似于@Akash,但这是我使用的另一种方法。它使用 str 而不是 lambda 因为 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")

p
pc_pyr

只需执行以下操作,您可以使用 --test = True

python 文件名 --test

parser.add_argument("--test" , default=False ,help="test ?", dest='test', action='store_true')

这与 4 年前的 this answer 有何不同?
s
scorpionipx

转换值:

def __arg_to_bool__(arg):
    """__arg_to_bool__

        Convert string / int arg to bool
    :param arg: argument to be converted
    :type arg: str or int
    :return: converted arg
    :rtype: bool
    """
    str_true_values = (
        '1',
        'ENABLED',
        'ON',
        'TRUE',
        'YES',
    )
    str_false_values = (
        '0',
        'DISABLED',
        'OFF',
        'FALSE',
        'NO',
    )

    if isinstance(arg, str):
        arg = arg.upper()
        if arg in str_true_values:
            return True
        elif arg in str_false_values:
            return False

    if isinstance(arg, int):
        if arg == 1:
            return True
        elif arg == 0:
            return False

    if isinstance(arg, bool):
        return arg

    # if any other value not covered above, consider argument as False
    # or you could just raise and error
    return False

[...]


args = ap.parse_args()
my_arg = options.my_arg
my_arg = __arg_to_bool__(my_arg)


F
FiddleStix

扩展gerardw的答案

parser.add_argument("--my_bool", type=bool) 不起作用的原因是对于任何非空字符串,bool("mystring") 都是 True,因此 bool("False") 实际上是 True

你想要的是

我的程序.py

import argparse

parser = argparse.ArgumentParser(description="My parser")

parser.add_argument(
        "--my_bool",
        choices=["False", "True"],
        )

parsed_args = parser.parse_args()

my_bool = parsed_args.my_bool == "True"

print(my_bool)
$ python my_program.py --my_bool False
False

$ python my_program.py --my_bool True
True

$ python my_program.py --my_bool true
usage: my_program.py [-h] [--my_bool {False,True}]
my_program.py: error: argument --my_bool: invalid choice: 'true' (choose from 'False', 'True')

l
lalit gangwar

我找到了将参数的默认值存储为 False 的好方法,当它出现在命令行参数中时,它的值应该为 true。

当您希望参数为真时的 cmd 命令:python main.py --csv

当你想要你的论点应该是假的时候:python main.py

import argparse
from ast import parse
import sys
parser = argparse.ArgumentParser(description='')
parser.add_argument('--csv', action='store_true', default = False 
,help='read from csv')
args = parser.parse_args()

if args.csv:
    print('reading from csv')

“store_true”已将默认值设置为 False