Pregunta

Estoy escribiendo un script de Python relativamente simple que admite un par de comandos diferentes. Los diferentes comandos admiten diferentes opciones y quiero poder pasar las opciones analizadas por Argparse al método correcto para el comando especificado.

La cadena de uso se ve así:

usage: script.py [-h]

            {a, b, c}
            ...
script.py: error: too few arguments

Puedo llamar fácilmente el método apropiado:

def a():
    ...

def b():
    ...

def c():
    ...

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.set_defaults(method = a)
    ...

    arguments = parser.parse_args()
    arguments.method()

Sin embargo, tengo que pasar argumentos a estos métodos y todos aceptan diferentes conjuntos de argumentos.

Actualmente, acabo de pasar el Namespace Objeto devuelto por Argparse, como así:

 def a(arguments):
     arg1 = getattr(arguments, 'arg1', None)
     ...

Esto parece un poco incómodo y hace que los métodos un poco más difíciles de reutilizar, ya que tengo que aprobar los argumentos como un dict o espacio de nombres en lugar de parámetros habituales.

De alguna manera, me gustaría definir los métodos con parámetros (como lo haría con una función normal) y aún poder llamarlos dinámicamente mientras pasa los parámetros apropiados. Al igual que:

def a(arg1, arg2):
    ...

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.set_defaults(method = a)
    ...

    arguments = parser.parse_args()
    arguments.method() # <<<< Arguments passed here somehow

¿Algunas ideas?

¿Fue útil?

Solución

Encontré una solución bastante buena:

import argparse

def a(arg1, arg2, **kwargs):
    print arg1
    print arg2

if __name__ == "__main__":
        parser = argparse.ArgumentParser()
        parser.set_defaults(method = a)
        parser.add_argument('arg1', type = str)
        parser.add_argument('arg2', type = str)

        arguments = parser.parse_args()
        arguments.method(**vars(arguments))

Por supuesto, hay un problema menor si los argumentos del método chocan con los nombres de los argumentos que usa Argparse, aunque creo que esto es preferible pasar el objeto del espacio de nombres y usar getattr.

Otros consejos

Probablemente esté tratando de lograr la funcionalidad que proporcionan los subcomandos:http://docs.python.org/dev/library/argparse.html#sub-commands

No estoy seguro de cuán práctico es esto, pero usando inspeccionar Puede dejar de lado el parámetro Extraneus ** Kwargs en sus funciones, como así:

import argparse
import inspect


def sleep(seconds=0):
    print "sleeping", seconds, "seconds"

def foo(a, b=2, **kwargs):
    print "a=",a
    print "b=",b
    print "kwargs=",kwargs

parser = argparse.ArgumentParser()

subparsers = parser.add_subparsers(title="subcommand")

parser_sleep = subparsers.add_parser('sleep')
parser_sleep.add_argument("seconds", type=int, default=0)
parser_sleep.set_defaults(func=sleep)

parser_foo = subparsers.add_parser('foo')
parser_foo.add_argument("-a", type=int, default=101)
parser_foo.add_argument("-b", type=int, default=201)
parser_foo.add_argument("--wacky", default=argparse.SUPPRESS)
parser_foo.set_defaults(func=foo)

args = parser.parse_args()

arg_spec = inspect.getargspec(args.func)
if arg_spec.keywords:
    ## convert args to a dictionary
    args_for_func = vars(args)
else:
    ## get a subset of the dictionary containing just the arguments of func
    args_for_func = {k:getattr(args, k) for k in arg_spec.args}

args.func(**args_for_func)

Ejemplos:

$ python test.py sleep 23
sleeping 23 seconds

$ python test.py foo -a 333 -b 444
a= 333
b= 444
kwargs= {'func': <function foo at 0x10993dd70>}

$ python test.py foo -a 333 -b 444 --wacky "this is wacky"
a= 333
b= 444
kwargs= {'func': <function foo at 0x10a321d70>, 'wacky': 'this is wacky'}

¡Que te diviertas!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top