in Python, laat het gebruik van Argarse alleen positieve gehele getallen

De titel vat vrijwel samen wat ik zou willen gebeuren.

Hier is wat ik heb, en hoewel het programma niet op een nonpositief geheel getal blaast, wil ik dat de gebruiker wordt geïnformeerd dat een nonpositief geheel getal in principe onzin is.

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

en de uitvoer:

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

Uitgang met een negatief:

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

Nu, natuurlijk, kon ik gewoon een if toe toevoegen om te bepalen if args.gamesnegatief is, maar ik was nieuwsgierig als er een manier was om het op de argparsete vangen niveau, om te profiteren van het automatische gebruik van het gebruik.

Idealiter zou het iets afdrukken met dit:

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'

Zoals SO:

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'

Voor nu doe ik dit, en ik denk dat ik gelukkig ben:

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

Antwoord 1, Autoriteit 100%

Dit zou mogelijk moeten zijn met behulp van type. U moet nog steeds een echte methode definiëren die dit voor u beslist:

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)

Dit is eigenlijk alleen een aangepast voorbeeld uit de perfect_squareFunctie in de Documenten op argparse.


Antwoord 2, Autoriteit 28%

typezou de aanbevolen optie zijn om omstandigheden / cheques aan te pakken, zoals in het antwoord van Yuushi.

In uw specifieke geval kunt u ook de parameter choicesgebruiken als uw bovenlimiet ook bekend is:

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

Opmerking: Gebruik rangein plaats van xrangevoor python 3.x


Antwoord 3, Autoriteit 4%

De snelle en vuile manier, als u een voorspelbare MAX en MIN voor uw ARG hebt, is gebruik choicesmet een bereik

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

Antwoord 4, Autoriteit 3%

Een eenvoudiger alternatief, vooral als subclassificatie argparse.ArgumentParser, is het initiëren van de validatie van binnenin de parse_argsmethode.

Binnen een dergelijke subklasse:

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

Deze techniek is mogelijk niet zo cool als een aangepaste callable, maar het doet de taak.


Over ArgumentParser.error(message):

Deze methode drukt een gebruiksbericht af inclusief het bericht naar de standaardfout en beëindigt het programma met een statuscode van 2.


Krediet: antwoord door jonatan


Antwoord 5, autoriteit 2%

In het geval dat iemand (zoals ik) deze vraag tegenkomt in een Google-zoekopdracht, hier is een voorbeeld van hoe je een modulaire aanpak kunt gebruiken om het meer algemene probleem van het toestaan ​​van argparse integers alleen in een gespecificeerd bereik:

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

Hiermee kun je iets doen als:

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 variabele foostaat nu alleen positieve gehele getallentoe, zoals de OP vroeg.

Merk op dat naast de bovenstaande formulieren ook gewoon een maximum mogelijk is met IntRange:

parser.add_argument('other', type=IntRange(imax=10))  # Must have other <= 10

Antwoord 6

Op basis van Yuushi’s antwoord kun je ook een eenvoudige hulpfunctie definiëren die kan controleren of een getal positief is voor verschillende numerieke typen:

def positive(numeric_type):
    def require_positive(value):
        number = numeric_type(value)
        if number <= 0:
            raise ArgumentTypeError(f"Number {value} must be positive.")
        return number
    return require_positive

De helperfunctie kan worden gebruikt om elk numeriek argumenttype als volgt te annoteren:

parser = argparse.ArgumentParser(...)
parser.add_argument("positive-integer", type=positive(int))
parser.add_argument("positive-float", type=positive(float))

Other episodes