jgritty
Tengo este código hasta ahora:
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()
No tiene sentido proporcionar un valor negativo para el número de juegos, pero type=int
permite cualquier número entero. Por ejemplo, si corro python simulate_many.py -g -2
, args.games
se establecerá en -2
y el programa continuará como si nada estuviera mal.
Me doy cuenta de que podría simplemente verificar explícitamente el valor de args.games
después de analizar los argumentos. Pero puedo hacer argparse
mismo comprobar esta condición? ¿Cómo?
Preferiría que funcionara de esa manera para que el mensaje de uso automático pueda explicar el requisito al usuario. Idealmente, la salida sería algo como:
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'
tal como actualmente maneja argumentos que no se pueden convertir a enteros:
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'
Yuushi
Esto debería ser posible utilizando type
. Aún deberá definir un método real que decida esto por usted:
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)
Esto es básicamente solo un ejemplo adaptado del perfect_square
función en el documentos en argparse
.
-
¿Tu función puede tener múltiples valores? ¿Cómo funciona?
– Tomás
1 de julio de 2016 a las 22:11
-
Si la conversión a
int
falla, ¿seguirá habiendo una salida legible? o deberíastry
raise
la conversión manualmente para eso?– NO
12/09/2017 a las 15:58
-
@MrZ Dará algo como
error: argument foo: invalid check_positive value: 'foo=<whatever>'
. Simplemente podría agregar untry:
…except ValueError:
a su alrededor que vuelve a generar una excepción con un mejor mensaje de error.– Yuushi
13 de septiembre de 2017 a las 5:33
aneroide
type
sería la opción recomendada para manejar condiciones/controles, como en la respuesta de Yuushi.
En tu caso concreto, también puedes utilizar el choices
parámetro si también se conoce su límite superior:
parser.add_argument('foo', type=int, choices=xrange(5, 10))
Nota: Usar range
en lugar de xrange
para pitón 3.x
-
Me imagino que esto sería bastante ineficiente, ya que generaría un rango y luego lo recorrería para validar su entrada. Un rápido
if
es mucho más rápido.– TravisThomas
8 oct 2014 a las 18:27
-
@ trav1th De hecho, podría serlo, pero es un ejemplo de uso de los documentos. Además, he dicho en mi respuesta que la respuesta de Yuushi es la que hay que buscar. Bueno para dar opciones. Y en el caso de argparse, sucede una vez por ejecución, usa un generador (
xrange
) y no requiere código adicional. Esa compensación está disponible. Depende de cada uno decidir qué camino tomar.– aneroide
9 oct 2014 a las 11:45
-
Para ser más claro sobre el punto de jgritty sobre la respuesta del autor de ben, las opciones = x rango (0,1000) darán como resultado que la lista completa de enteros del 1 al 999 inclusive se escriba en su consola cada vez que use –help o si un argumento no válido es proporcionó. No es una buena opción en la mayoría de las circunstancias.
– biomiker
29 de abril de 2016 a las 16:21
-
Si usa una amplia gama de números, no creo que esta sea la solución más limpia, ya que –help generará los números enteros que se procesan y arruinará por completo la vista de –help
– JimShapedCoding
9 de junio de 2021 a las 8:17
La forma rápida y sucia, si tiene un máximo predecible y un mínimo para su argumento, es usar choices
con un rango
parser.add_argument('foo', type=int, choices=xrange(0, 1000))
-
La desventaja es la horrible salida.
– jgritty
2 de enero de 2013 a las 6:05
-
enfásis en suciosupongo.
– ben autor
2 de enero de 2013 a las 6:07
-
Para ser más claro sobre el punto de jgritty, las opciones = x rango (0,1000) darán como resultado que la lista completa de números enteros del 1 al 999 inclusive se escriba en su consola cada vez que use –help o si se proporciona un argumento no válido. No es una buena opción en la mayoría de las circunstancias.
– biomiker
29 de abril de 2016 a las 16:21
Palgeuer
En caso de que alguien (como yo) encuentre esta pregunta en una búsqueda de Google, aquí hay un ejemplo de cómo usar un enfoque modular para resolver claramente el problema más general de permitir números enteros argparse solo en un rango especificado:
# 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")
Esto le permite hacer algo como:
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
La variable foo
ahora solo permite enteros positivoscomo preguntó el OP.
Tenga en cuenta que, además de las formas anteriores, también es posible un máximo con IntRange
:
parser.add_argument('other', type=IntRange(imax=10)) # Must have other <= 10
Asclepio
Una alternativa más simple, especialmente si se subclasifica argparse.ArgumentParser
es iniciar la validación desde dentro del parse_args
método.
Dentro de tal subclase:
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
Esta técnica puede no ser tan genial como un invocable personalizado, pero hace el trabajo.
Acerca de ArgumentParser.error(message)
:
Este método imprime un mensaje de uso que incluye el mensaje de error estándar y finaliza el programa con un código de estado de 2.
Crédito: respuesta de jonatan
-
O por lo menos, reemplazando
print "-g/--games: must be positive."; sys.exit(1)
con soloparser.error("-g/--games: must be positive.")
. (Uso como en la respuesta de jonatan).– aneroide
24 de enero de 2019 a las 6:27
Jan Heinrich Reimer
Según la respuesta de Yuushi, también puede definir una función de ayuda simple que puede verificar si un número es positivo para varios tipos numéricos:
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
La función auxiliar se puede utilizar para anotar cualquier tipo de argumento numérico como este:
parser = argparse.ArgumentParser(...)
parser.add_argument("positive-integer", type=positive(int))
parser.add_argument("positive-float", type=positive(float))
-
O por lo menos, reemplazando
print "-g/--games: must be positive."; sys.exit(1)
con soloparser.error("-g/--games: must be positive.")
. (Uso como en la respuesta de jonatan).– aneroide
24 de enero de 2019 a las 6:27