Python'da argparse kullanarak yalnızca pozitif tamsayılara izin verin


164

Başlık, ne olmasını istediğimi hemen hemen özetliyor.

İşte sahip olduğum şey ve program pozitif olmayan bir tamsayı havaya uçurmasa da, kullanıcının pozitif olmayan bir tamsayının temelde saçmalık olduğu konusunda bilgilendirilmesini istiyorum.

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

Ve çıktı:

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

Negatif çıktı:

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

Şimdi, açık bir if args.gamesşekilde negatif olup olmadığını belirlemek için bir if ekleyebilirim , ancak argparseotomatik kullanım baskısından yararlanmak için seviyeyi yakalamanın bir yolu olup olmadığını merak ettim .

İdeal olarak, buna benzer bir şey basar:

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'

Şöyle ki:

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'

Şimdilik bunu yapıyorum ve sanırım mutluyum:

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

Yanıtlar:


244

Bunu kullanmak mümkün olmalıdır type. Yine de sizin için karar veren gerçek bir yöntem tanımlamanız gerekir:

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)

Bu temelde sadece bir uyarlanmış örnektir perfect_squareişlev docs üzerinde argparse.


1
İşlevinizin birden fazla değeri olabilir mi? Bu nasıl çalışıyor?
Tom

2
Dönüşüm intbaşarısız olursa, yine de okunabilir bir çıktı olacak mı? Yoksa try raisebunun için manuel olarak dönüşüm mü yapmalısınız ?
NOhs

4
@MrZ Böyle bir şey verecek error: argument foo: invalid check_positive value: 'foo=<whatever>'. Etrafına daha iyi bir hata mesajı ile bir istisna oluşturan bir try:... ekleyebilirsiniz except ValueError:.
Yuushi

59

type Yuushi'nin cevabında olduğu gibi koşulları / kontrolleri ele almak için önerilen seçenek olurdu.

Özel durumunuzda, choicesüst sınırınız da biliniyorsa parametreyi de kullanabilirsiniz :

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

Not: python 3.x rangeyerine kullanınxrange


3
Bunun bir aralık oluşturduğundan ve daha sonra bisiklete binerek girdinizi doğruladığınızdan, bunun oldukça verimsiz olacağını düşünüyorum. Bir hızlı ifçok daha hızlı.
TravisThomas

2
@ trav1th Aslında olabilir, ancak dokümanlar tarafından örnek bir kullanımdır. Ayrıca cevabımda Yuushi'nin cevabının cevap olduğunu söyledim. Seçenekler vermek güzel. Ve argparse durumunda, yürütme başına bir kez gerçekleşir, bir jeneratör ( xrange) kullanır ve ek kod gerektirmez. Bu değiş tokuş yapılabilir. Hangi yöne gideceğinize karar vermek için her birine kadar.
aneroid

16
Jgritty'nin ben yazarın cevabı üzerindeki daha net olması için, seçimler = xrange (0,1000), --help veya sağlanan. Çoğu durumda iyi bir seçim değil.
biomiker

9

Eğer argümanız için tahmin edilebilir bir maksimum ve min varsa, hızlı ve kirli yol choicesbir aralık ile kullanmaktır

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

24
Olumsuz tarafı iğrenç çıktı.
jgritty

6
kirli vurgu, sanırım.
ben yazar

4
Jgritty'nin noktasından daha açık olmak gerekirse, choices = xrange (0,1000), --help'i her kullandığınızda veya geçersiz bir bağımsız değişken sağlandığında konsolunuza 1 ile 999 arasında bir tam sayı listesinin yazılmasına neden olur. Çoğu durumda iyi bir seçim değil.
biomiker

8

Daha basit bir alternatif, özellikle de alt sınıflama argparse.ArgumentParser, parse_argsyöntemin içinden doğrulamanın başlatılmasıdır .

Böyle bir alt sınıfın içinde:

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

Bu teknik, özel bir çağrılabilir kadar serin olmayabilir, ancak işi yapar.


Hakkında ArgumentParser.error(message):

Bu yöntem, mesajı içeren bir kullanım iletisini standart hataya yazdırır ve programı 2 durum koduyla sonlandırır.


Kredi bilgileri: answer by jonatan


Ya da en azından, print "-g/--games: must be positive."; sys.exit(1)sadece yerine koymak parser.error("-g/--games: must be positive."). ( Jonatan'ın cevabındaki gibi kullanım .)
aneroid

3

Birisinin (benim gibi) bir Google aramasında bu soruya rastlaması durumunda, argparse tamsayılarına yalnızca belirli bir aralıkta izin verme konusundaki genel sorunu düzgün bir şekilde çözmek için modüler bir yaklaşımın nasıl kullanılacağına dair bir örnek :

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

Bu, aşağıdaki gibi bir şey yapmanıza izin verir:

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

Değişken fooartık istenen OP gibi sadece pozitif tamsayılara izin vermektedir .

Yukarıdaki formlara ek olarak, aşağıdakilerle sadece bir maksimumun mümkün olduğunu unutmayın IntRange:

parser.add_argument('other', type=IntRange(imax=10))  # Must have other <= 10
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.