使用 Python argparse 支持任意数量的相关命名参数
Support arbitrary number of related named arguments with Python argparse
我想支持一个命令行界面,用户可以在其中声明任意数量的样本,每个样本对应一个或多个输入文件。像这样:
$ 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
命令将使我能够访问所有用户提供的参数,但那些未明确声明的参数将不会分组到索引数据结构中。对于这些我需要仔细检查参数列表,向前看有多少后续值对应于当前标志等。
有什么方法可以解析这些选项,而不必(几乎)从头开始重新实现参数解析器的大部分内容?
使用 click 而不是 argparse
。
很可能会完成您正在寻找的那种事情
引用:
$ click_
Click is a Python package for creating beautiful command line
interfaces in a composable way with as little code as necessary.
It's the "Command Line Interface Creation Kit". It's highly
configurable but comes with sensible defaults out of the box.
It aims to make the process of writing command line tools quick and
fun while also preventing any frustration caused by the inability to
implement an intended CLI API.
Click in three points:
- arbitrary nesting of commands
- automatic help page generation
supports lazy loading of subcommands at runtime
Read the docs at http://click.pocoo.org/
click 的一个重要特性是能够构建子命令(有点像使用 git 或 image magic covert),这应该允许您将命令行结构化为:
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
的参数以及名为 process
和 combine
的函数指定的输入文件,不带参数组合。
将数字或键设为单独的参数值,并将相关参数收集在嵌套列表中会更简单。
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
中没有任何内容可以直接支持这一点。已经提出了各种各样的建议,但都归结为自己解析这些对。
您增加了复杂性,即每个键的参数数量是可变的。这排除了将“--sample1=input1”作为简单字符串处理的可能性。
argparse
扩展了众所周知的 POSIX
命令行标准。但是如果你想超越那个,那么准备在 (sys.argv) 之前或 argparse
之后(parse_known_args
extras
)处理参数。
如果您可以接受稍微不同的语法,即:
$ 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']])
正如我在评论中提到的:
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')
这种方法的主要缺点是自动帮助生成不会涵盖这些字段。
我想支持一个命令行界面,用户可以在其中声明任意数量的样本,每个样本对应一个或多个输入文件。像这样:
$ 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
命令将使我能够访问所有用户提供的参数,但那些未明确声明的参数将不会分组到索引数据结构中。对于这些我需要仔细检查参数列表,向前看有多少后续值对应于当前标志等。
有什么方法可以解析这些选项,而不必(几乎)从头开始重新实现参数解析器的大部分内容?
使用 click 而不是 argparse
。
引用:
$ click_
Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the "Command Line Interface Creation Kit". It's highly configurable but comes with sensible defaults out of the box.
It aims to make the process of writing command line tools quick and fun while also preventing any frustration caused by the inability to implement an intended CLI API.
Click in three points:
- arbitrary nesting of commands
- automatic help page generation
supports lazy loading of subcommands at runtime
Read the docs at http://click.pocoo.org/
click 的一个重要特性是能够构建子命令(有点像使用 git 或 image magic covert),这应该允许您将命令行结构化为:
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
的参数以及名为 process
和 combine
的函数指定的输入文件,不带参数组合。
将数字或键设为单独的参数值,并将相关参数收集在嵌套列表中会更简单。
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
中没有任何内容可以直接支持这一点。已经提出了各种各样的建议,但都归结为自己解析这些对。
您增加了复杂性,即每个键的参数数量是可变的。这排除了将“--sample1=input1”作为简单字符串处理的可能性。
argparse
扩展了众所周知的 POSIX
命令行标准。但是如果你想超越那个,那么准备在 (sys.argv) 之前或 argparse
之后(parse_known_args
extras
)处理参数。
如果您可以接受稍微不同的语法,即:
$ 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']])
正如我在评论中提到的:
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')
这种方法的主要缺点是自动帮助生成不会涵盖这些字段。