¿Cómo agregar argumentos opcionales o una vez?

¿Cómo puedo agregar un argumento que es opcional y no se debe especificar varias veces?

Válido:

$ ./my.py $ ./my.py --arg MyArgValue 

Inválido:

 $ ./my.py --arg MyArgValue --arg ThisIsNotValid 

Si agrego un argumento como:

 parser.add_argument('--arg', type=str) 

El ejemplo no válido da como resultado una cadena ThisIsNotValid . Yo esperaría un error del analizador.

Cree una acción personalizada que genere una excepción si el mismo argumento se ve dos veces. Cuando el analizador detecta la excepción, imprime el uso y un mensaje de error bien formateado.

 import argparse class Highlander(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): if getattr(namespace, self.dest, None) is not None: raise argparse.ArgumentError(self, 'There can be only one.') setattr(namespace, self.dest, values) parser = argparse.ArgumentParser() parser.add_argument('-f', action=Highlander) print (parser.parse_args('-f 1 -f 2'.split())) 

Esto se siente como una solución pirata, pero te dará lo que quieres. Usa la acción de append :

 parser.add_argument('-arg', type=str, action='append') args = parser.parse_args() if len(args.arg) > 1: sys.exit("Only one argument is allowed for '-arg'") elif len(args.arg) == 1: # elif is because it is valid for the length to be 0 args.arg = args.arg[0] 

La acción anexada creará una lista desde la línea de comando que consta de todos los valores de todas las veces que se llamó este argumento. Si la longitud de esta lista es más larga que una, hubo un error.

De esta manera, puede obtener los valores de la línea de comando, y si hay más de uno, puede detectar esto como un error y notificar al usuario.

Puede usar esto para obtener el número de ocurrencias con esto y generar un error si es más de uno.

‘añadir’: esto almacena una lista y agrega cada valor de argumento a la lista. Esto es útil para permitir que una opción se especifique varias veces. Ejemplo de uso:

 import argparse import sys parser = argparse.ArgumentParser() parser.add_argument('-arg', type='str', action='append') args = parser.parse_args() if len(args.arg) > 1: sys.exit('Invalid') MyArgValue = args.arg[0] 

La documentación sobre ‘anexar’ se puede encontrar aquí para obtener más detalles:

http://docs.python.org/dev/library/argparse.html#action

Desafortunadamente, no hay una manera más simple con argparse. Eso es presumiblemente porque este no es un caso de uso común suficiente. Por lo general, este recuento se utiliza para casos booleanos, como el modo detallado. Cuando tiene cadenas anexas, como comstackdores con la ruta de inclusión, etc., por lo general se respetan.

Tal vez, también podría usar nargs y solo usar args.arg_name [0] e ignorar el rest. Aquí puedes encontrar la documentación de nargs para eso:

http://docs.python.org/dev/library/argparse.html#nargs

He probado una solución que utiliza un grupo de mutually_exclusive_group . La idea es definir un grupo que incluya --arg dos veces. El analizador mantiene una lista seen_non_default_actions , y verifica que no haya conflictos de grupo exclusivos antes de tomar acción en una nueva cadena de argumento. Si --arg ya está presente en esta lista, la siguiente llamada entraría en conflicto con ella y generaría un error.

Hay un par de problemas con este enfoque.

1) las acciones existentes no se pueden agregar a un nuevo grupo de exclusión mutua. En https://stackoverflow.com/a/18555236/901925 ilustro un kludge que se da cuenta de eso. También cita un parche propuesto que lo hace más fácil.

2) actualmente parse_args agrega la acción a la lista seen_non_default_actions y luego verifica si hay conflictos. Esto significa que el primer --arg entrará en conflicto consigo mismo. La solución es cambiar el orden. Primero verifique si hay conflictos, luego agregue la acción a la lista.

 import my_argparse as argparse # use a customized argparse parser = argparse.ArgumentParser(prog="PROG", formatter_class=argparse.MultiGroupHelpFormatter) # use a custom formatter than can handle overlapping groups action = parser.add_argument('--arg', help='use this argument only once') # define a group with two copies of this action as per patch issue10984 group = parser.add_mutually_exclusive_group(action, action) args = parser.parse_args() 

Cuando se llama con varios argumentos, produce:

 $ python3 test.py -h usage: PROG [-h] [--arg ARG | --arg ARG] optional arguments: -h, --help show this help message and exit --arg ARG use this argument only once $ python3 test.py --arg test Namespace(arg='test') $ python3 test.py --arg test --arg next usage: PROG [-h] [--arg ARG | --arg ARG] PROG: error: argument --arg: not allowed with argument --arg 

Sin embargo, me pregunto si esta es una manera lo suficientemente intuitiva de decir “usa este argumento solo una vez”. ¿La línea de uso [--arg ARG | --arg ARG] [--arg ARG | --arg ARG] transmitir eso? ¿Y el argument --arg: not allowed with argument --arg mensaje de error argument --arg: not allowed with argument --arg suficientemente claro?