web-dev-qa-db-ja.com

Argparseによるブール値の解析

"--foo True"または "--foo False"として書かれたブール値のコマンドライン引数を解析するためにargparseを使いたいと思います。例えば:

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_boolTrueに評価されます。 cmd_line["--my_bool", ""]に変更してもbool("")Falseに評価されるので、これは驚くべきことです。

Argparseで"False""F"、およびそれらの小文字をFalseに解析する方法を教えてください。

441
SuperElectric

以前の提案を使用した、argparseからの「正しい」解析エラーを使用したさらに別の解決策:

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'が位置的な引数をとらえるかもしれないということです この関連する質問 そして このargparseバグレポート を見てください。

156
Maxim

これを行うためのもっと規範的な方法は、次のとおりです。

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?
687
mgilson

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

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だけがあります。

このコードは '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を整数に変換します。

31
hpaulj

1ライナー:

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

これは、デフォルト値を設定するための追加の行がない別のバリエーションです。ブール値には常に値が割り当てられているため、事前チェックなしで論理ステートメントで使用できます。

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

私は同じ問題を探していました、そしてimhoは素晴らしい解決策です:

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

そしてそれを使って文字列を真偽値にパースします。

18
susundberg

@mgilsonが言ったことに加えて、--flag--no-flagが同時に使用されないことを強制するのを簡単にする ArgumentParser.add_mutually_exclusive_group(required=False) メソッドもあることに注意すべきです。

13
foo

これは私が期待していることすべてに有効です。

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')
8
Stumpy Joe Pete

非常によく似た方法は、使用することです。

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

また、引数に--featureを設定した場合

 command --feature

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

6
dl.meteo

もっと簡単な方法は以下のように使うことです。

parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
5
arunkumarreddy
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)
2

最も簡単な方法はchoicesを使用することです。

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オプションを追加できます。

2
gerardw

最も規範的な方法は次のようになると思います。

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

ENSURE = config.ensure is None
1