Python:提取用argparse创建的参数

时间:2018-03-15 23:45:52

标签: python python-2.7 argparse

我有一个名为simple_example.py的文件,它由2个函数组成:

# import the necessary packages
import argparse


class simple:

    @staticmethod
    def func1():

        # construct the argument parse and parse the arguments
        ap = argparse.ArgumentParser()
        ap.add_argument("-n", "--name", help="name of the user", default='host')
        ap.add_argument('-num', '--number', required=True, help='choose a number')
        args = vars(ap.parse_args())

        # display a friendly message to the user
        print("Hi there {}, it's nice to meet you! you chose {}".format(args['name'], args['age']))


    @staticmethod
    def func2():

        # construct the argument parse and parse the arguments
        ap = argparse.ArgumentParser()
        ap.add_argument("-n", "--name", help="name of the user", default='host')
        ap.add_argument('-num', '--number', required=True, help='choose a number')
        ap.add_argument("-g", "--greet", help="say greetings", default='hello')
        args = vars(ap.parse_args())

        # display a friendly message to the user
        print("{} there {}, it's nice to meet you! you chose {}".format(args['greet'], args['name'], args['age']))

我希望能够从命令行调用func1()或func2(),因此,我从此link创建了另一个名为pyrun.py的文件

# !/usr/bin/env python
# make executable in bash chmod +x PyRun

import sys
import inspect
import importlib
import os


if __name__ == "__main__":
    cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile(inspect.currentframe()))[0]))
    if cmd_folder not in sys.path:
        sys.path.insert(0, cmd_folder)

    # get the second argument from the command line
    methodname = sys.argv[1]

    # split this into module, class and function name
    modulename, classname, funcname = methodname.split(".")

    # get pointers to the objects based on the string names
    themodule = importlib.import_module(modulename)
    theclass = getattr(themodule, classname)
    thefunc = getattr(theclass, funcname)

    # pass all the parameters from the third until the end of what the function needs & ignore the rest
    args = inspect.getargspec(thefunc)

    print(args)

但是,ArgSpec中的args(args = [],varargs = None,keywords = None,defaults = None)显示空列表。

  1. 如何从func1或func2中提取参数?

  2. 有没有更好的方法从命令行运行func1或func2?

2 个答案:

答案 0 :(得分:1)

您可能想要使用sub-commands。以下是使用子命令的示例实现。

import argparse

def func1(args):

    print("Hi there {}, it is nice to meet you! You chose {}.".format(args.name, args.number))

def func2(args):

    print("{} there {}, it is nice to meet you! You chose {}.".format(args.greet, args.name, args.number))

#
# The top-level parser
#
parser = argparse.ArgumentParser('top.py', description='An example sub-command implementation')

#
# General sub-command parser object
#
subparsers = parser.add_subparsers(help='sub-command help')

#
# Specific sub-command parsers
#
cmd1_parser = subparsers.add_parser('cmd1', help='The first sub-command')
cmd2_parser = subparsers.add_parser('cmd2', help='The second sub-command')

#
# Assign the execution functions
#
cmd1_parser.set_defaults(func=func1)
cmd2_parser.set_defaults(func=func2)

#
# Add the common options
#
for cmd_parser in [cmd1_parser, cmd2_parser]:
    cmd_parser.add_argument('-n',   '--name',   default='host', help='Name of the user')
    cmd_parser.add_argument('-num', '--number', required=True,  help='Number to report')

#
# Add command-specific options
#
cmd2_parser.add_argument('-g', '--greet', default='hello', help='Greeting to use')

#
# Parse the arguments
#
args = parser.parse_args()

#
# Invoke the function
#
args.func(args)

示例输出:

$ python ./top.py cmd1 -n Mark -num 3    
Hi there Mark, it is nice to meet you! You chose 3.

$ python ./top.py cmd2 -n Bob -num 7 -g Hello
Hello there Bob, it is nice to meet you! You chose 7.

当然,帮助功能适用于每个子命令。

$ python ./top.py cmd2 -h

usage: top.py cmd2 [-h] [-n NAME] -num NUMBER [-g GREET]

optional arguments:
  -h, --help            show this help message and exit
  -n NAME, --name NAME  Name of the user
  -num NUMBER, --number NUMBER
                        Number to report
  -g GREET, --greet GREET
                        Greeting to use

答案 1 :(得分:0)

如果我将第一个代码块放在一个文件中,我可以将其导入ipython会话并运行您的2个函数:

In [2]: import stack49311085 as app
In [3]: app.simple
Out[3]: stack49311085.simple

ipython标签扩展(使用某种形式的inspect)向我显示该模块有一个simple类,该类本身有两个静态函数。

我可以致电func1,并收到argparse错误消息:

In [4]: app.simple.func1()
usage: ipython3 [-h] [-n NAME] -num NUMBER
ipython3: error: the following arguments are required: -num/--number
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2

同样适用于func2

In [7]: app.simple.func2()
usage: ipython3 [-h] [-n NAME] -num NUMBER [-g GREET]
ipython3: error: the following arguments are required: -num/--number
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2

parse_args作为默认值解析sys.argv[1:]列表,显然不符合其要求。

def foo(argv=None):
    parser = ....
    ....
    args = parse.parse_args(argv=argv)
    return args

是一个更有用的包装器。有了这个,我可以传递一个测试argv列表,并返回解析后的Namespace。如果我没有给出这样的列表,它将使用sys.argv默认值。在测试解析器时,我想返回和/或显示整个Namespace

我还没有使用inspect来试图找出你想要用它做什么,或者如何纠正它。您不需要inspect在这样的导入模块中运行代码。

我可以通过修改sys.argv

来测试导入的解析器
In [8]: import sys
In [9]: sys.argv
Out[9]: 
['/usr/local/bin/ipython3',
 '--pylab',
 '--nosep',
 '--term-title',
 '--InteractiveShellApp.pylab_import_all=False']
In [10]: sys.argv[1:] = ['-h']
In [11]: app.simple.func2()
usage: ipython3 [-h] [-n NAME] -num NUMBER [-g GREET]

optional arguments:
  -h, --help            show this help message and exit
  -n NAME, --name NAME  name of the user
  -num NUMBER, --number NUMBER
                        choose a number
  -g GREET, --greet GREET
                        say greetings
An exception has occurred, use %tb to see the full traceback.

SystemExit: 0

或关注help

In [12]: sys.argv[1:] = ['-num=42', '-nPaul', '-gHI']
In [13]: app.simple.func2()
...
---> 30         print("{} there {}, it's nice to meet you! you chose {}".format(args['greet'], args['name'], args['age']))
KeyError: 'age'

糟糕,代码中出现错误。您要求args['age'],但没有定义具有该名称的解析器参数。这就是为什么我喜欢打印args命名空间 - 的一部分 - 以确保它设置了我期望的所有属性。

通常我们不会为不同的输入使用不同的解析器。可以根据您自己的sys.avgv[1]测试来做到这一点,但请记住,该字符串仍将位于您的解析器读取的sys.argv[1:]列表中。而是编写一个可以处理各种输入样式的解析器。另一个答案中提到的subparser是一个选项。另一种方法是根据args.greet属性的值进行操作。如果不使用,它将是默认值。