Python - передать аргументы различным методам из Argparse
Я пишу относительно простой Python script, который поддерживает пару разных команд. Различные команды поддерживают разные параметры, и я хочу иметь возможность передавать параметры, обработанные argparse, правильному методу для указанной команды.
Строка использования выглядит так:
usage: script.py [-h]
{a, b, c}
...
script.py: error: too few arguments
Я могу легко вызвать соответствующий метод:
def a():
...
def b():
...
def c():
...
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.set_defaults(method = a)
...
arguments = parser.parse_args()
arguments.method()
Однако мне приходится передавать аргументы этим методам, и все они принимают разные наборы аргументов.
В настоящее время я просто передаю объект Namespace
, возвращаемый argparse, например:
def a(arguments):
arg1 = getattr(arguments, 'arg1', None)
...
Это кажется немного неудобным и делает методы немного сложнее повторно использовать, поскольку я должен передавать аргументы как dict
или пространство имен, а не как обычные параметры.
Я хотел бы каким-то образом определить методы с параметрами (как и обычные функции) и все еще иметь возможность их динамически вызывать при передаче соответствующих параметров. Например:
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
Любые идеи?
Ответы
Ответ 1
Я нашел довольно приятное решение:
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))
Конечно, есть небольшая проблема, если аргументы метода сталкиваются с именами аргументов argparse, но я думаю, что это предпочтительнее передавать объект пространства имен и использовать getattr
.
Ответ 2
Вероятно, вы пытаетесь достичь той функциональности, которую предоставляют подкоманды:
http://docs.python.org/dev/library/argparse.html#sub-commands
Ответ 3
Не уверен, насколько это практично, но используя inspect, вы можете оставить посторонний параметр ** kwargs для своих функций, например так:
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)
Примеры:
$ 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'}
Удачи!