Python - 从Argparse传递参数到不同的方法

16 投票
3 回答
23780 浏览
提问于 2025-04-17 01:42

我正在写一个相对简单的Python脚本,它支持几种不同的命令。这些命令有不同的选项,我想把通过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()

不过,我需要给这些方法传递参数,而它们接受的参数类型各不相同。

现在,我只是把argparse返回的Namespace对象传递过去,像这样:

 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

有什么好的想法吗?

3 个回答

0

我不太确定这个方法有多实用,但你可以通过使用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'}

祝你玩得开心!

2

你可能想要实现的是子命令所提供的功能:

http://docs.python.org/dev/library/argparse.html#sub-commands

8

我找到了一种不错的解决办法:

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使用的参数名字冲突,那就会有个小问题。不过我觉得这样比到处传递Namespace对象并用getattr要好。

撰写回答