如何从 Python 3 中的现有程序创建带有 argparse 的子解析器?

Posted

技术标签:

【中文标题】如何从 Python 3 中的现有程序创建带有 argparse 的子解析器?【英文标题】:How to create subparser with argparse from existing program in Python 3? 【发布时间】:2019-02-05 18:29:20 【问题描述】:

原帖:

如果一个可执行文件mini_program.py 使用argparse 并具有以下结构:

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-X', '--attribute_matrix', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    parser.add_argument('-y', '--target_vector', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    opts = parser.parse_args()

if __name__ == "__main__":
    main()

如何创建一个控制器程序parent_program.py 使用argparse(我认为是subparser?)具有与以下类似的用法:

python parent_program.py --help

blah-blah list of programs that can be used

然后使用子程序:

python parent_program.py mini_program --help

-X description
-y description
etc...

所有参数如何从mini_program.py 向上传播到parent_program.py

编辑(更具体的错误消息):

程序

import argparse
def main():
    parser = argparse.ArgumentParser()
    # Subprograms
    subprograms = parser.add_subparsers(title="subprograms")
    # ============
    # mini-program
    # ============
    parser_miniprogram = subprograms.add_parser("miniprogram")

    # Input
    parser_miniprogram.add_argument('-X', '--attribute_matrix', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    parser_miniprogram.add_argument('-y', '--target_vector', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    opts = parser.parse_args()
    opts_miniprogram = parser_miniprogram.parse_args()
    print(opts_miniprogram.__dict__)

if __name__ == "__main__":
    main()

检查以确保文档正常工作

# parent program
python parent_program.py --help
usage: parent_program.py [-h] miniprogram ...

optional arguments:
  -h, --help     show this help message and exit

subprograms:
  miniprogram

# miniprogram
python parent_program.py miniprogram --help
usage: parent_program.py miniprogram [-h] [-X ATTRIBUTE_MATRIX]
                                     [-y TARGET_VECTOR]

optional arguments:
  -h, --help            show this help message and exit
  -X ATTRIBUTE_MATRIX, --attribute_matrix ATTRIBUTE_MATRIX
                        Input: Path/to/Tab-separated-value.tsv
  -y TARGET_VECTOR, --target_vector TARGET_VECTOR
                        Input: Path/to/Tab-separated-value.tsv

尝试运行它:

python parent_program.py miniprogram -X ../../Data/X_iris.noise_100.tsv.gz -y ../../Data/y_iris.tsv
usage: parent_program.py miniprogram [-h] [-X ATTRIBUTE_MATRIX]
                                     [-y TARGET_VECTOR]
parent_program.py miniprogram: error: unrecognized arguments: miniprogram

【问题讨论】:

也许像this 这样的东西很有用。或者,正如您提到的,使用subparsers。 @pazitos10 那个 git 例子真的很有趣。我从未见过 argparse 使用 int hat 方式。我尝试了 subparser 方式并编辑了我的答案。 您无需致电:parser_miniprogram.parse_args()parser 中的子解析器机制负责调用带有剩余参数的子解析器。 【参考方案1】:

父程序可以有类似的代码

import mini_program
import sys
<do its own parsing>
if 'use_mini':
    <modify sys.argv>
    mini_program.main()

如前所述,导入 mini_program 不会运行其解析器。但是调用它的main 会,但是使用它在sys.argv 中找到的列表。

父解析器的编写方式应该接受它需要的参数,并且不会阻塞mini想要的输入,'-X'和'-y'。然后它将这些“额外”值放入修改后的sys.argvmini 解析器可以处理。

parse_known_args 是接受未知参数的一种方式, https://docs.python.org/3/library/argparse.html#partial-parsing

nargs=argparse.REMAINDER、https://docs.python.org/3/library/argparse.html#nargs 是另一种收集剩余参数以进行传递的方式。

如果mini main 写成:

def main(argv=None):
    parser = argparse.ArgumentParser()
    parser.add_argument('-X', '--attribute_matrix', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    parser.add_argument('-y', '--target_vector', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    opts = parser.parse_args(argv)

它可以被调用

mini_program.main(['-X', 'astring','-y','another'])

也就是说,使用明确的argv 列表,而不是通过sys.argv 工作。

阻止主解析器响应“-h”帮助可能会很棘手。 subparsers 可能是最干净的方式。

您可以将子解析器与mini main 的调用结合使用。我现在不会尝试解决这些细节。

另一种定义main的方法是:

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-X', '--attribute_matrix', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    parser.add_argument('-y', '--target_vector', type=str, help = 'Input: Path/to/Tab-separated-value.tsv')
    return parser

并将其用作

 opts = main().parse_args()
 opts = mini_program.main().parse_args()

也就是说,使用main定义解析器,但是延迟解析。

【讨论】:

将此与***.com/questions/14071135/…结合使用【参考方案2】:

我的实际解决方案是对上述内容的适应:

# Controller
def main(argv=None):
    parser = argparse.ArgumentParser(prog="parent_program", add_help=True)
    parser.add_argument("subprogram")
    opts = parser.parse_args(argv)
    return opts.subprogram


# Initialize
if __name__ == "__main__":
    # Get the subprogram 
    subprogram = main([sys.argv[1]])
    module = importlib.import_module(subprogram)
    module.main(sys.argv[2:])

【讨论】:

以上是关于如何从 Python 3 中的现有程序创建带有 argparse 的子解析器?的主要内容,如果未能解决你的问题,请参考以下文章

Python 3:更改现有函数参数的默认值?

Python3 - 如何从现有抽象类定义抽象子类?

C-Numpy:如何从现有数据创建固定宽度的字符串数组

如何将新服务部署到 Google App Engine 中的现有应用程序?

Python Pandas从现有数据帧的所有行组合创建新的数据帧

PyQT4:从 Ui 获取现有 python 程序的文本