En Python, usando argparse, permita solo números enteros positivos
⏰ 6 minutos de lectura
jgritty
El título resume bastante bien lo que me gustaría que sucediera.
Esto es lo que tengo, y aunque el programa no explota en un número entero no positivo, quiero que el usuario sepa que un número entero no positivo es básicamente una tontería.
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()
Y la salida:
python simulate_many.py -g 20
Setting up...
Playing games...
....................
Salida con un negativo:
python simulate_many.py -g -2
Setting up...
Playing games...
Ahora, obviamente podría agregar un si para determinar if args.games es negativo, pero tenía curiosidad por saber si había una manera de atraparlo en el argparse nivel, para aprovechar la impresión de uso automático.
Idealmente, imprimiría algo similar a esto:
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'
Por ahora estoy haciendo esto, y supongo que estoy feliz:
if args.games <= 0:
parser.print_help()
print "-g/--games: must be positive."
sys.exit(1)
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ías tryraise 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 un try: … 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:
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 previsto. 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
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
Asclepio
Una alternativa más simple, especialmente si se subclasifica argparse.ArgumentParseres 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.
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
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
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:
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información