【问题标题】:Support arbitrary number of related named arguments with Python argparse使用 Python argparse 支持任意数量的相关命名参数
【发布时间】:2017-10-31 06:09:17
【问题描述】:

我想支持一个命令行界面,用户可以在其中声明任意数量的样本,每个样本对应一个或多个输入文件。像这样的:

$ myprogram.py \
      --foo bar \
      --sample1 input1.tsv \
      --sample2 input2a.tsv input2b.tsv input2c.tsv \
      --sample3 input3-filtered.tsv \
      --out output.tsv

这个想法是选项键将匹配模式--sample(\d+),并且每个键将使用所有后续参数作为选项值,直到遇到下一个---前缀标志。对于显式声明的参数,这是argparse 模块通过nargs='+' 选项支持的常见用例。但由于我需要支持任意数量的参数,我无法明确声明它们。

parse_known_args 命令将允许我访问所有用户提供的参数,但那些未明确声明的参数将不会被分组到索引数据结构中。对于这些,我需要仔细检查参数列表,向前看有多少后续值对应于当前标志等。

有什么方法可以解析这些选项,而不必从头开始(几乎)重新实现参数解析器的大部分?

【问题讨论】:

  • 您是否考虑过即时构建 argparser?计算您在命令行上拥有的--sample 的数量并使用它来构建适当的argparser?有点迂回,但它让 argparse 完成了繁重的工作。

标签: python argparse


【解决方案1】:

如果您可以使用稍微不同的语法,即:

$ myprogram.py \
  --foo bar \
  --sample input1.tsv \
  --sample input2a.tsv input2b.tsv input2c.tsv \
  --sample input3-filtered.tsv \
  --out output.tsv

如果参数名称不包含数字,但仍执行分组,请尝试以下操作:

parser.add_argument('--sample', action='append', nargs='+')

它产生一个列表列表,即。 --sample x y --sample 1 2 将产生Namespace(sample=[['x', 'y'], ['1', '2']])

【讨论】:

  • 我以前用过action="append",但从来没有在这种情况下使用过。好建议!
【解决方案2】:

正如我在评论中提到的:

import argparse

argv = "myprogram.py \
      --foo bar \
      --sample1 input1.tsv \
      --sample2 input2a.tsv input2b.tsv input2c.tsv \
      --sample3 input3-filtered.tsv \
      --out output.tsv"

parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.add_argument('--out')
for x in range(1, argv.count('--sample') + 1):
    parser.add_argument('--sample' + str(x), nargs='+')
args = parser.parse_args(argv.split()[1:])

给予:

print args
Namespace(foo='bar', out='output.tsv', sample1=['input1.tsv'], sample2=['input2a.tsv', 'input2b.tsv', 'input2c.tsv'], sample3=['input3-filtered.tsv'])

对于真正的sys.argv,您可能必须将argv.count 替换为稍长的' '.join(sys.argv).count('--sample')

这种方法的主要缺点是自动帮助生成不会涵盖这些领域。

【讨论】:

  • 您的代码适用于字符串argv,但不适用于已经拆分的sys.argv。帮助将列出命令行中也给出的任何sample#,以及“-h”。但是要显示通用的 sample#,您必须使用自定义的 usage 参数。
【解决方案3】:

将数字或键设置为单独的参数值,并将相关参数收集到嵌套列表中会更简单。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.add_argument('--out')
parser.add_argument('--sample', nargs='+', action='append', metavar=('KEY','TSV'))

parser.print_help()

argv = "myprogram.py \
      --foo bar \
      --sample 1 input1.tsv \
      --sample 2 input2a.tsv input2b.tsv input2c.tsv \
      --sample 3 input3-filtered.tsv \
      --out output.tsv"
argv = argv.split()
args = parser.parse_args(argv[1:])
print(args)

产生:

1031:~/mypy$ python3 stack44267794.py -h
usage: stack44267794.py [-h] [--foo FOO] [--out OUT] [--sample KEY [TSV ...]]

optional arguments:
  -h, --help            show this help message and exit
  --foo FOO
  --out OUT
  --sample KEY [TSV ...]
Namespace(foo='bar', out='output.tsv', 
    sample=[['1', 'input1.tsv'], 
            ['2', 'input2a.tsv', 'input2b.tsv', 'input2c.tsv'], 
            ['3', 'input3-filtered.tsv']])

有关于收集一般key:value 对的问题。 argparse 中没有任何内容可以直接支持这一点。已经提出了各种各样的建议,但都归结为自己解析这些对。

Is it possible to use argparse to capture an arbitrary set of optional arguments?

您已经添加了每个键的参数数量是可变的复杂性。这排除了将“--sample1=input1”作为简单字符串处理的可能性。

argparse 扩展了众所周知的POSIX 命令行标准。但是,如果您想超越这一点,请准备好在 (sys.argv) 之前或 argparse (parse_known_args extras) 之后处理参数。

【讨论】:

    【解决方案4】:

    使用click 而不是argparse 很可能完成您正在寻找的事情。

    引用:

    $ click_

    Click 是一个 Python 包,用于创建漂亮的命令行 以可组合的方式使用尽可能少的代码进行接口。 它是“命令行界面创建工具包”。这是高度 可配置,但带有开箱即用的合理默认值。

    它旨在使编写命令行工具的过程快速而 有趣的同时还可以防止因无法 实现预期的 CLI API。

    点击三点:

    • 命令的任意嵌套
    • 自动帮助页面生成
    • 支持在运行时延迟加载子命令

      阅读文档http://click.pocoo.org/

    click 的一个重要特性是能够构造子命令,(有点像使用 git 或 image 魔法隐蔽),这应该允许您将命令行构造为:

    myprogram.py \
      --foo bar \
      --sampleset input1.tsv \
      --sampleset input2a.tsv input2b.tsv input2c.tsv \
      --sampleset input3-filtered.tsv \
      combinesets --out output.tsv
    

    甚至:

    myprogram.py \
      --foo bar \
      process input1.tsv \
      process input2a.tsv input2b.tsv input2c.tsv \
      process input3-filtered.tsv \
      combine --out output.tsv
    

    这可能更简洁,在这种情况下,您的代码将包含名为 --foo--out 的参数,以及名为 processcombine 的函数,将使用指定的输入文件调用进程并与 no 组合参数。

    【讨论】:

    • 除非您可以通过点击实际展示如何执行此操作——或者至少指出要使用哪些功能——否则这并不能真正算作答案。
    猜你喜欢
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2021-03-06
    • 2010-09-19
    • 2011-06-13
    • 2017-10-13
    • 2021-12-23
    • 1970-01-01
    相关资源
    最近更新 更多