Python Argparse,处理可重复的项集

Python Argparse,处理可重复的项集,python,argparse,Python,Argparse,是否可以创建一组相关且可重复的可选参数 假设我有三个参数-a,-b,-c组成一个集合, -a是必需的,但-b和-c是可选的。(更新) 我希望能够指定多组这些 Script.py -a 1 -b 2 -c 3 -a 4 -c 6 -a 7 -b 8 -a 10 这将被解析为dict列表,如下所示 [ {"a":1, "b":2, "c":3}, {"a":4, "c":6}, {"a":7, "b":8}, {"a":10} ] 我们可以迭代命令

是否可以创建一组相关且可重复的可选参数

假设我有三个参数-a,-b,-c组成一个集合, -a是必需的,但-b和-c是可选的。(更新)

我希望能够指定多组这些

Script.py -a 1 -b 2 -c 3    -a 4 -c 6   -a 7 -b 8    -a 10
这将被解析为dict列表,如下所示

[
    {"a":1, "b":2, "c":3},
    {"a":4, "c":6},
    {"a":7, "b":8},
    {"a":10}
]

我们可以迭代命令行参数,并将它们逐个添加到集合中,如果我们到达'-a',它标志着另一个集合的开始,我们将创建一个新集合。但此代码示例不检查无效的用户输入

import sys

def get_pairs(iterator, start):
    sets = []
    for val in iterator:
        if val == start:
            sets.append({})
        sets[-1][val] = next(iterator)
    return sets

print get_pairs(iter(sys.argv[1:]), '-a')

使参数可重复的一种方法是使用“追加”操作类型:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', action='append')
parser.add_argument('-b', action='append')
parser.add_argument('-c', action='append')
argv = '-a 1 -b 2 -c 3    -a 4 -c 6   -a 7 -b 8    -a 10'
args = parser.parse_args(argv.split())
print args
产生:

Namespace(a=['1', '4', '7', '10'], b=['2', '8'], c=['3', '6'])
[{'a': 1, 'c': 3, 'b': 2}, {'a': 4, 'c': 6}, {'a': 7, 'b': 8}, {'a': 10}]
不幸的是,它确实丢失了一些信息。无法将“4”与“6”而不是“8”相关联

如果使用“---”分隔参数块,则此迭代解析器可能完成以下工作:

parser = argparse.ArgumentParser()
# SUPPRESS keeps the default None out of the namespace
parser.add_argument('-a', type=int, default=argparse.SUPPRESS, required=True)
parser.add_argument('-b', type=int, default=argparse.SUPPRESS)
parser.add_argument('-c', type=int, default=argparse.SUPPRESS)
argv = '-a 1 -b 2 -c 3  --  -a 4 -c 6 --  -a 7 -b 8  --  -a 10'

arglist = []
rest = argv.split()
while rest:
    args,rest = parser.parse_known_args(rest)
    rest = rest[1:]  # remove the 1st '--'
    print args
    arglist.append(vars(args))
print arglist
制作:

Namespace(a=1, b=2, c=3)
Namespace(a=4, c=6)
Namespace(a=7, b=8)
Namespace(a=10)

[{'a': 1, 'c': 3, 'b': 2}, 
 {'a': 4, 'c': 6}, 
 {'a': 7, 'b': 8}, 
 {'a': 10}]
我不确定它是否足够健壮。我将
-a
设为必填项,因此从其中一个组中忽略它将导致错误


或者改编法扎德的迭代器:

def by_sets(iterator, start):
    set = []
    for val in iterator:
        if set and val == start:
            yield set
            set = [val]
        else:
            set.append(val)
    yield set

argv = '-a 1 -b 2 -c 3  -a 4 -c 6 -a 7 -b 8  -a 10'
# print list(by_sets(argv.split(), '-a'))
# [['-a', '1', '-b', '2', '-c', '3'], ['-a', '4', '-c', '6'],... ['-a', '10']]

arglist = []
for aset in by_sets(argv.split(), '-a'):
    arglist.append(vars(parser.parse_args(aset)))
print arglist
more_itertools.split_before(arg_list, lambda arg: arg == '-a')
产生:

Namespace(a=['1', '4', '7', '10'], b=['2', '8'], c=['3', '6'])
[{'a': 1, 'c': 3, 'b': 2}, {'a': 4, 'c': 6}, {'a': 7, 'b': 8}, {'a': 10}]
循环也可以写成理解:

[vars(parser.parse_args(aset)) for aset in by_sets(argv.split(), '-a')]
谢谢你,我发现你的回答很有帮助。实现farzad迭代器的另一种方法:

def by_sets(iterator, start):
    set = []
    for val in iterator:
        if set and val == start:
            yield set
            set = [val]
        else:
            set.append(val)
    yield set

argv = '-a 1 -b 2 -c 3  -a 4 -c 6 -a 7 -b 8  -a 10'
# print list(by_sets(argv.split(), '-a'))
# [['-a', '1', '-b', '2', '-c', '3'], ['-a', '4', '-c', '6'],... ['-a', '10']]

arglist = []
for aset in by_sets(argv.split(), '-a'):
    arglist.append(vars(parser.parse_args(aset)))
print arglist
more_itertools.split_before(arg_list, lambda arg: arg == '-a')

如何在命令行上指定设置边界?使用多个空格是一种直观的帮助,但是当shell解析命令以执行时,多个空格不会有帮助。因此,您的Python应用程序将只收到:
-a1-b2-c3-a4-c6-b8-a10
。现在如何定义集合?每个集合的元素都被排序,这样你就知道你已经到达了另一个集合,因为你以前有c,现在你已经到达了a,这是一个有效的假设吗?空格只是为了清楚地显示可能的交互作用。我同意没有必要的参数就没有好的方法把它们分成集合,谢谢你指出这一点,我应该提到-a是一个必需的参数,这让我们能够识别集合的边界。我想对每个参数简单地使用list并将它们压缩为元组,但如果缺少一些可选的arg,这将是不正确的。很好,谢谢,接受hpaulj的解决方案,因为问题是argparse特定的。尽管farzad提供了有用的注释和代码。谢谢你们两位