argparseによるブール値の解析


616

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


40
@mgilsonの回答 を1 行で説明しますparser.add_argument('--feature', dest='feature', default=False, action='store_true')。このソリューションは、常にboolTrueまたはの型を取得することを保証します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)別のスタックオーバーフローの質問からです
Trevor Boyd Smith

8
どのように何かparser.add_argument('--my_bool', action='store_true', default=False)
AruniRC 2017年

回答:


276

前の提案を使用したさらに別の解決策ですが、次の「正しい」解析エラーがあります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>

それでもデフォルト値を使用します(ユーザー設定に固有)。そのアプローチの(間接的に関連する)欠点の1つは、「ナグ」が位置の引数をキャッチする可能性があることです。この関連する質問このargparseバグレポートを参照してください。


4
nargs = '?' ゼロまたは1つの引数を意味します。docs.python.org/3/library/argparse.html#nargs
Maxim

1
私はこれが好きですが、default = NICEに相当するとエラーが発生するため、別のことをする必要があります。
マイケルマシューズ2017

2
@MarcelloRomani str2boolはPythonの意味での型ではなく、上記で定義された関数です。どこかに含める必要があります。
マキシム

4
のコードはstr2bool(v)で置き換えることができます bool(distutils.util.strtobool(v))。出典:stackoverflow.com/a/18472142/2436175
Antonio

4
おそらく、この方法ではif args.nice:、引数がFalseに設定されている場合、引数がbeacuse で設定されているかどうかを確認できないため、条件を通過することはありません。これは右のであれば、多分それからリストを返すために優れているstr2boolとして機能し、セットリストconstのように、パラメータ[True][False]。私が間違っている場合は私を修正してください
NutCracker 2018

889

これを行うためのより標準的な方法は、

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と見なされます(文字列からboolへのキャストのため)。 多分関連する問題
イルカ

41
そうです、これが期待どおりに機能しない理由はないと思います。安全性のチェックもエラーメッセージもないため、これは非常に誤解を招く可能性があります。
2013

69
@mgilson-私が誤解を招くのは、type = boolを設定できることです。エラーメッセージは表示されませんが、 "False"と "True"の両方の文字列引数では、ブール値の変数でTrueが返されます(型キャストはPythonで機能します)。したがって、type = boolは明らかにサポートされていない(警告、エラーなどを出力する)か、または有用で直感的に期待される方法で機能する必要があります。
2013

14
@dolphin-それぞれ、私は同意しません。私はその振る舞いは本来あるべき姿であり、Pythonの禅「特別なケースはルールを破るほど特別ではない」と一致していると思います。ただし、これについて強く感じた場合は、さまざまなpython メーリングリストの 1つで取り上げてみませんか?、あなたは力がある誰か説得のチャンスがありかもしれない行うこの問題について何かを。あなたが私を説得できたとしても、あなたは私を説得することに成功しただけで、私は
開発者で

15
Python bool()関数が何をすべきか、またはargparseが何を受け入れるべきかについて議論していtype=fnますか?すべてのargparseチェックはfn呼び出し可能です。fn文字列引数を1つ取り、値を返すことを想定しています。の動作fnはプログラマーの責任であり、ではありませんargparse's
hpaulj 2013年

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 2016

4
これまたはmgilsonの答えは受け入れられた答えであるはずです-OPが望ん--flag Falseでいたとしても、SOの答えの一部は、方法だけでなく、彼らが何を解決しようとしているのかに関するものでなければなりません。そこ行うには絶対にない理由はないはずです--flag False--other-flag True、その後、ブール値に文字列を変換するために、いくつかのカスタムパーサーを使用... action='store_true'action='store_false'ブールフラグを使用するための最良の方法です
kevlarr

6
@cowlinatorなぜSOが最終的に「述べられた質問」に答えることについているのですか?よると、独自のガイドライン、anwer ... can be “don’t do that”, but it should also include “try this instead”(私には、少なくとも)答えを暗示する際に適切な深いを行く必要があります。質問を投稿する私たちの一部がより良い/ベストプラクティスなどのガイダンスから恩恵を受けることができる時は間違いありません。そうは言っても、多くの場合(または誤って)想定している回答に対する不満は完全に有効です。
kevlarr 2018

2
ユーザーが機能を明示的に指定していない場合に3番目の値が必要な場合は、最後の行をparser.set_defaults(feature=None)
Alex Che

2
help=この引数のエントリを追加したい場合、どこに行けばよいでしょうか?でadd_mutually_exclusive_group()呼び出し?片方または両方のadd_argument()通話で?どこか別の場所?
ケンウィリアムズ

57

デフォルト値を設定するための追加の行がない別のバリエーションがあります。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")

6
この答えは過小評価されていますが、その単純さは素晴らしいです。設定しようとしないでください。設定しないと、required=True常にTrue引数が取得されます。
S

1
してくださいNEVERブール値またはnonetypeのようなものに等価演算子を使用していません。 代わりにISを使用してください
webKnjaZ

2
冗長なブール文字列を要求する代わりに、ブール値を設定するためにフラグの存在を単にチェックするだけなので、これは受け入れられたよりも良い答えです。(ヨーヨー、ブール値が好きだと聞いた...ブール値をブール値と一緒に設定するためにブール値を与えた!)
サイフォン

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
onelinerファンのために良い、また、それが少し改善される可能性:type=lambda x: (str(x).lower() in ['true','1', 'yes'])
火ブイ

35

type=booltype='bool'意味し、何を意味するのかについて、いくつかの混乱があるようです。1つ(または両方)は、「関数を実行する」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>}}

多くのアクションが定義されていますが、デフォルトのタイプの1つだけです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取るため、それについて知る必要はありません。両方のカスタム値を定義するスタックオーバーフローの例はたくさんあります。


前の説明から明らかでない場合でも、bool()「文字列を解析する」ことを意味しません。Pythonのドキュメントから:

bool(x):標準の真偽テスト手順を使用して、値をブール値に変換します。

これと比較して

int(x):数値または文字列xを整数に変換します。


3
または、以下を使用します:parser.register( 'type'、 'bool'、(lambda x:x.lower()in( "yes"、 "true"、 "t"、 "1")))
Matyas

17

私は同じ問題を探していましたが、かなりの解決策は次のとおりです:

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

そして、それを使用して、上記のように文字列をブール値に解析します。


5
あなたがこのルートに行くつもりなら、私が提案するかもしれませんdistutils.util.strtobool(v)
CivFan 2017

1
distutils.util.strtobool1または0を返すことはなく、実際のブール。
CMCDragonkai

14

非常に似た方法は次のようにすることです:

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

コマンドで引数--featureを設定した場合

 command --feature

type --featureを設定しない場合、引数はTrueになります。引数のデフォルトは常にFalseです!


1
この方法には他の答えが克服するいくつかの欠点がありますか?これは、OP(この場合は私)が望んでいたことを実現する、最も簡単で簡潔なソリューションであるようです。大好きです。
Simon O'Hanlon

2
単純ですが、質問には答えません。OPは、指定できる引数が必要です--feature False
Astariul '28


12

これは私が期待するすべてのことで機能します:

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

優秀な!この答えでいきます。1 回_str_to_bool(s)変換してからs = s.lower()テストしif s not in {'true', 'false', '1', '0'}、最後にを調整しましたreturn s in {'true', '1'}
Jerry101 2018

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

2
これはかわいいですが、evalが悪であることを知らないユーザーがスクリプトにコピーアンドペーストするだけで、実際に公開するのは非常に危険です。
アルネ

@アルネ、良い点。ただし、善意のあるユーザーが誤って悪意のあることを行うのはかなり難しいようです。
ラッセル

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と評価されます。ユーザーに常に明示的に選択肢を指定させたい場合は、required = Trueオプションを追加できます。



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、yes、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に似ていますが、ここに私が使用した別のアプローチがあります。これは、使用するstrよりも、lambdaPythonがあるため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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.