2011-03-21 10 views
16

Estoy tratando de usar argparse para analizar los argumentos de línea de comando para un programa en el que estoy trabajando. Esencialmente, necesito admitir múltiples argumentos posicionales separados dentro de los argumentos opcionales, pero no puedo hacer que argparse funcione en esta situación. En el programa real, estoy usando una acción personalizada (necesito almacenar una instantánea del espacio de nombres cada vez que se encuentra un argumento posicional), pero el problema que tengo se puede replicar con la acción append:Argumentos posicionales múltiples con Python y argparse

Me gustaría que esto dé como resultado el espacio de nombres (a=True, b=True, input=['fileone', 'filetwo', 'filethree']), pero no puedo ver cómo hacerlo, si es que puede hacerlo. No puedo ver nada en los documentos o en Google, que dicen de una forma u otra si esto es posible, aunque es bastante posible (¿es probable?). He pasado por alto algo. ¿Alguien tiene alguna sugerencia?

+0

¿Qué tal si cambias '' -a'' y '' -b'' a '' append'', luego modificas el espacio de nombres resultante a lo que quieres? –

Respuesta

7

srgerg tenía razón acerca de la definición de argumentos posicionales. Para obtener el resultado que desea, debe aceptarlos como argumentos opcionales y modificar el espacio de nombres resultante de acuerdo con sus necesidades.

Se puede utilizar una acción personalizada:

class MyAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 

     # Set optional arguments to True or False 
     if option_string: 
      attr = True if values else False 
      setattr(namespace, self.dest, attr) 

     # Modify value of "input" in the namespace 
     if hasattr(namespace, 'input'): 
      current_values = getattr(namespace, 'input') 
      try: 
       current_values.extend(values) 
      except AttributeError: 
       current_values = values 
      finally: 
       setattr(namespace, 'input', current_values) 
     else: 
      setattr(namespace, 'input', values) 

parser = argparse.ArgumentParser() 
parser.add_argument('-a', nargs='+', action=MyAction) 
parser.add_argument('-b', nargs='+', action=MyAction) 
parser.add_argument('input', nargs='+', action=MyAction) 

Y esto es lo que obtienes:

>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree']) 
Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree']) 

O puede modificar el espacio de nombres dado como resultado la siguiente manera:

>>> import argparse 
>>> parser = argparse.ArgumentParser() 
>>> parser.add_argument('-a', nargs='+') 
>>> parser.add_argument('-b', nargs='+') 
>>> parser.add_argument('input', nargs='+') 
>>> result = parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree']) 

>>> inputs = [] 
>>> inputs.extend(result.a) 
>>> inputs.extend(result.b) 
>>> inputs.extend(result.input) 

>>> modified = argparse.Namespace(
     a = result.a != [], 
     b = result.b != [], 
     input = inputs) 

Y esto es lo que obtienes:

>>> modified 
Namespace(a=True, b=True, input=['filetwo', 'filethree', 'fileone']) 

Sin embargo, ambos métodos dan como resultado un código menos legible y menos sostenible. Tal vez sea mejor cambiar la lógica del programa y hacerlo de otra manera.

+0

Gracias para los ejemplos. Me gustaría ser capaz de soportar múltiples argumentos posicionales, pero al mismo tiempo prefiero no tener que mantener soluciones como esta. Parece que tengo que tomar algunas decisiones ... – Blair

16

No se pueden intercalar los interruptores (es decir, -a y -b) con los argumentos posicionales (es decir, fileone, filetwo y filethree) de esta manera. Los interruptores deben aparecer antes o después de los argumentos posicionales, no en el medio.

Además, para tener múltiples argumentos posicionales, debe especificar el parámetro nargs en add_argument. Por ejemplo:

parser.add_argument('input', nargs='+') 

Esto le dice argparse consumir uno o más argumentos posicionales y los añade a una lista. Vea el argparse documentation para más información. Con esta línea, el código:

parser.parse_args(['-a', '-b', 'fileone', 'filetwo', 'filethree']) 

resultados en:

Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree']) 
+0

No es exactamente lo que el OP quería. '' parser.parse_args (['fileone', '-a', 'filetwo', '-b', 'filethree'] '' da el mismo mensaje de error. –

+2

Cierto, pero como explico al principio de mi respuesta , no puede intercalar los interruptores (por ejemplo, '-a') con los argumentos posicionales (por ejemplo, fileone, filetwo, ...). Debe poner los interruptores antes o después de los argumentos de posición. – srgerg

+0

Gracias por la respuesta, no pude para encontrar un sí o no definitivo en la documentación en cuanto a si esto era compatible – Blair

2

La acción 'append' tiene más sentido con un opcional:

parser.add_argument('-i', '--input',action='append') 
parser.parse_args(['-i','fileone', '-a', '-i','filetwo', '-b', '-i','filethree']) 

Usted puede intercalar opcionales con posicionales separados ('INPUT1 INPUT2 -a -b input3'), pero no se puede intercalar opcionales dentro de un multiitem posicional Pero puedes lograr esto con un análisis de dos pasos.

import argparse 
parser1 = argparse.ArgumentParser() 
parser1.add_argument('-a', action='store_true') 
parser1.add_argument('-b', action='store_true') 
parser2 = argparse.ArgumentParser() 
parser2.add_argument('input', nargs='*') 

ns, rest = parser1.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree']) 
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree'] 

ns = parser2.parse_args(rest, ns) 
# Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree']) 

http://bugs.python.org/issue14191 es un parche propuesto que va a hacer esto con una sola llamada a:

parser.parse_intermixed_args(['fileone', '-a', 'filetwo', '-b', 'filethree']) 
1

Me parece que hpaulj está en el camino correcto, pero haciendo las cosas un poco más complicadas de lo necesario. Sospecho que Blair está buscando algo similar al comportamiento del antiguo módulo optparse y realmente no necesita la lista de argumentos de entrada en el campo de entradas del objeto args. Sólo quiere

import argparse 
parser = argparse.ArgumentParser() 
parser.add_argument('-a', action='store_true') 
parser.add_argument('-b', action='store_true') 
opts, args = parser.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree']) 
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree'] 

En la lengua vernácula de optparse, las "opciones" están disponibles en TPO, y la lista de posiblemente intercalan otros "argumentos" son en args.

Cuestiones relacionadas