Pythonでは、argparseを使用して、正の整数のみを許可します


164

タイトルは私が何をしたいのかを大まかに要約しています。

ここに私が持っているものがあり、プログラムは非正の整数で爆破しませんが、非正の整数は基本的にナンセンスであることをユーザーに知らせたいです。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-g", "--games", type=int, default=162,
                    help="The number of games to simulate")
args = parser.parse_args()

そして出力:

python simulate_many.py -g 20
Setting up...
Playing games...
....................

負の出力:

python simulate_many.py -g -2
Setting up...
Playing games...

さて、明らかにifを追加してif args.gamesネガティブと判断することもできargparseますが、自動使用印刷を利用するために、そのレベルでトラップする方法があるかどうか知りたいと思いました。

理想的には、次のようなものを出力します。

python simulate_many.py -g a
usage: simulate_many.py [-h] [-g GAMES] [-d] [-l LEAGUE]
simulate_many.py: error: argument -g/--games: invalid int value: 'a'

そのようです:

python simulate_many.py -g -2
usage: simulate_many.py [-h] [-g GAMES] [-d] [-l LEAGUE]
simulate_many.py: error: argument -g/--games: invalid positive int value: '-2'

今のところ私はこれをやっていて、私は幸せだと思います:

if args.games <= 0:
    parser.print_help()
    print "-g/--games: must be positive."
    sys.exit(1)

回答:


244

これはを利用して可能になるはずtypeです。これを決定する実際のメソッドを定義する必要があります。

def check_positive(value):
    ivalue = int(value)
    if ivalue <= 0:
        raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)
    return ivalue

parser = argparse.ArgumentParser(...)
parser.add_argument('foo', type=check_positive)

これは、基本的にはわずかに適応例であるperfect_square関数のドキュメントargparse


1
関数に複数の値を設定できますか?それはどのように機能しますか?
トム

2
への変換がint失敗した場合でも、読み取り可能な出力はありますか?それともtry raise手動で変換する必要がありますか?
NOH's

4
@MrZそれはのようなものを与えるでしょうerror: argument foo: invalid check_positive value: 'foo=<whatever>'。より適切なエラーメッセージで例外を再発生させるtry:...を簡単に追加できexcept ValueError:ます。
Yuushi

59

type Yuushiの回答のように、条件/チェックを処理するための推奨オプションです。

特定のケースでは、choices上限もわかっている場合は、パラメーターを使用することもできます。

parser.add_argument('foo', type=int, choices=xrange(5, 10))

注:のrange代わりにxrangepython 3.xを使用してください


3
範囲を生成し、それを循環して入力を検証するため、これはかなり非効率的だと思います。クイックifははるかに高速です。
TravisThomas 2014年

2
@ trav1th確かにそうかもしれませんが、それはドキュメントからの使用例です。また、私は私の回答の中で、雄牛の答えは行くべきものであると述べました。オプションを与えるのに良い。そして、argparseの場合、それは実行ごとに1回発生し、ジェネレーター(xrange)を使用し、追加のコードを必要としません。そのトレードオフは利用可能です。どちらに進むかを決めるのは、各自です。
アネロイド2014年

16
benの作者の答えに対するjgrittyのポイントを明確にするために、choices = xrange(0,1000)は、-helpを使用するたびに、または無効な引数がある場合に、1から999までの整数のリスト全体をコンソールに書き込みます。提供されます。ほとんどの場合、適切な選択ではありません。
biomiker 2016

9

引数に予測可能な最大値と最小値がある場合choicesは、範囲を指定して使用するのが手っ取り早い方法です。

parser.add_argument('foo', type=int, choices=xrange(0, 1000))

24
欠点は恐ろしい出力です。
jgritty 2013年

6
汚れに重点を置いていると思います。
ベンの作成者

4
jgrittyのポイントを明確にするために、choices = xrange(0,1000)は、-helpを使用するたびに、または無効な引数が指定された場合に、1から999までの整数のリスト全体をコンソールに書き込みます。ほとんどの場合、適切な選択ではありません。
biomiker 2016

8

特にサブクラス化の場合、より簡単な方法argparse.ArgumentParserは、parse_argsメソッド内から検証を開始することです。

そのようなサブクラスの内部:

def parse_args(self, args=None, namespace=None):
    """Parse and validate args."""
    namespace = super().parse_args(args, namespace)
    if namespace.games <= 0:
         raise self.error('The number of games must be a positive integer.')
    return namespace

このテクニックは、カスタムのcallableほどクールではないかもしれませんが、うまくいきます。


についてArgumentParser.error(message)

このメソッドは、標準エラーへのメッセージを含む使用方法メッセージを出力し、ステータスコード2でプログラムを終了します。


クレジット:jonatanによる回答


または、少なくとも、print "-g/--games: must be positive."; sys.exit(1)単にに置き換えparser.error("-g/--games: must be positive.")ます。ジョナタンの答えのような使用法。)
アネロイド

3

(私のような)誰かがGoogle検索でこの質問に遭遇した場合、モジュール化されたアプローチを使用して、指定された範囲でのみ argparse整数を許可するというより一般的な問題を適切に解決する方法の例を次に示します

# Custom argparse type representing a bounded int
class IntRange:

    def __init__(self, imin=None, imax=None):
        self.imin = imin
        self.imax = imax

    def __call__(self, arg):
        try:
            value = int(arg)
        except ValueError:
            raise self.exception()
        if (self.imin is not None and value < self.imin) or (self.imax is not None and value > self.imax):
            raise self.exception()
        return value

    def exception(self):
        if self.imin is not None and self.imax is not None:
            return argparse.ArgumentTypeError(f"Must be an integer in the range [{self.imin}, {self.imax}]")
        elif self.imin is not None:
            return argparse.ArgumentTypeError(f"Must be an integer >= {self.imin}")
        elif self.imax is not None:
            return argparse.ArgumentTypeError(f"Must be an integer <= {self.imax}")
        else:
            return argparse.ArgumentTypeError("Must be an integer")

これにより、次のようなことが可能になります。

parser = argparse.ArgumentParser(...)
parser.add_argument('foo', type=IntRange(1))     # Must have foo >= 1
parser.add_argument('bar', type=IntRange(1, 7))  # Must have 1 <= bar <= 7

変数fooは、OPのように、正の整数のみを許可するようになりました。

上記のフォームに加えて、次のものでも最大数だけが可能IntRangeです。

parser.add_argument('other', type=IntRange(imax=10))  # Must have other <= 10
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.