在Python中声明一组可选参数内的位置参数

时间:2014-11-29 15:31:23

标签: python arguments argparse

我想在Python中使用argparse来声明参数如下:

./get_efms_by_ids [-h] [-v] [inputfile [1 3 4 9] [-c 11..18] [20 25 40]]

在这种情况下我想做的是:
如果使用inputfile,则可以使用两种类型的可选参数:1 3 4 9c 11..18或两者。如果我不输入inputfile,则必须缺少可选参数。

例如: 我可以向您展示命令行使用的一些示例:

./get_efms_by_ids Vacf.txt // default: get 1 or 10 first lines in Vacf.txt
./get_efms_by_ids Vacf.txt 1 3 4 9 // get the lines that indexes: 1 3 4 9 in Vacf.txt
./get_efms_by_ids Vacf.txt c 11..18 22 25 29 // get the lines that indexes are from 11 to 18, then the lines 22, 25, 29
./get_efms_by_ids c 11.. 18 // shows a readable error message
./get_efms_by_ids 1 3 4 9 // shows a readable error message

可以在以下示例中使用args='?'args='*'

parser = argparse.ArgumentParser(description='Selecting some Elementary Flux Modes by indexes.',version='1.0')
parser.add_argument('efm_matrix_file', type=file, help='give the name of the efms matrix file')
parser.add_argument('ids', nargs='?', help='give the indexes of the chosen efms')
parser.add_argument('-i','--indexes',nargs='*', help='give the begin and start indexes of the chosen efms')

但结果并不符合本文开头提出的目的。

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:3)

首先,我会放弃-c选项。您不需要-c..来表示一系列值。这样可以简化您对

之类的调用
./get_efms_by_ids [-h] [-v] [inputfile [index ...]]

其中每个index可以是单个整数,也可以是lower..upper指定的范围。

参数解析器可以是一个简单的

def index_type(s):
    try:
        return int(s)
    except ValueError:
        try:
            return map(int, s.split(".."))
        except:
            raise ArgumentTypeError("Invalid index: %s" % (s,))

p = ArgumentParser()
p.add_argument("-h")
p.add_argument("-v")
p.add_argument("inputfile", nargs="?")
p.add_argument("indices", nargs="*", type=index_type)
args = p.parse_args()

if not (args.inputfile is None or os.path.exists(args.inputfile)):
    sys.exit("Invalid file name: %s" % (args.inputfile,))

在解析之后,您必须检查第一个位置参数(如果有)是否是有效文件,因为任何字符串可以是有效的文件名。

index_type函数只是在解析过程中如何转换每个索引(无论是整数还是范围)的一个示例。

答案 1 :(得分:1)

我采取了与chepner不同的方法,但借用了一些chepner的想法:放弃-c选项并使用修改后的index_type()

代码

#!/usr/bin/env python
import argparse
from itertools import chain

def index_type(s):
    try:
        return [int(s)]
    except ValueError:
        try:
            start, stop = map(int, s.split('..'))
            return range(start, stop + 1)
        except:
            raise argparse.ArgumentTypeError("Invalid index: %s" % (s,))

def get_options():
    parser = argparse.ArgumentParser()
    parser.add_argument('-v')
    parser.set_defaults(fileinput=None)

    options, remaining = parser.parse_known_args()
    if remaining:
        parser = argparse.ArgumentParser()
        parser.add_argument('fileinput', type=argparse.FileType())
        parser.add_argument('selected_lines', nargs='*', type=index_type)
        parser.parse_args(remaining, namespace=options)

        # Convert a nested list into a set of line numbers
        options.selected_lines = set(chain.from_iterable(options.selected_lines))

        # If the command line does not specify the line numbers, assume a default
        if not options.selected_lines:
            options.selected_lines = set(index_type('1..10'))

    return options

if __name__ == '__main__':
    options = get_options()

    # If the command line contains a file name, loop through the file and process only the lines
    # requested
    if options.fileinput is not None:
        for line_number, line in enumerate(options.fileinput, 1):
            if line_number in options.selected_lines:
                line = line.rstrip()
                print '{:>4} {}'.format(line_number, line)

讨论

  • argparse模块允许使用可选参数,但fileinput不能是可选的,因为它是位置参数 - 这就是argparse的操作方式
  • 为了解决这个限制,我解析命令行两次:第一次获取-v标志。对于第一次解析,我使用parse_known_args()方法,忽略那些它不理解的参数。
  • 对于第二次解析,我处理remaning参数,假设第一个参数是文件名,后跟一系列行数
  • 解析行号非常棘手。最终目标是将"11..18 1 3 4 9"之类的内容转换为[1, 3, 4, 9, 11, 12, 13, 14, 15, 16, 17, 18]
  • 使用修改后的index_type()(感谢chepner),我能够将命令行从"11..18 1 3 4 9"解析为[11, 12, 13, 14, 15, 16, 17, 18], [1], [3], [4], [9]]
  • 下一步是将此嵌套列表转换为一组行号以便于查找
  • 作为奖励,如果命令行没有指定任何行号,我假设1..10
  • get_options返回后,options.fileinput将为None或文件句柄 - 无需打开要读取的文件。 options.selected_lines将是一组要选择的行号
  • 最后的任务是遍历这些行,如果已选中,则处理它。在我的情况下,我只是打印出来