【问题标题】:Argparse, handle repeatable set of itemsargparse,处理可重复的项目集
【发布时间】:2021-01-18 05:05:01
【问题描述】:

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

假设我有三个参数 -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}
]

【问题讨论】:

  • 你将如何在命令行中指定设置边界?使用多个空格是一种视觉帮助,但是当您的 shell 解析要执行的命令时,多个空格将无济于事。所以你的 Python 应用程序只会收到:-a 1 -b 2 -c 3 -a 4 -c 6 -b 8 -a 10。你现在如何定义集合?是否每个集合的元素都已排序是一个有效的假设,所以你知道你已经到达另一个集合,因为你之前有 c,现在你已经到达 a我>?
  • 这些空间只是为了清楚地显示可能的交互。我同意没有好的方法可以在没有必需参数的情况下将它们分成集合,感谢您指出这一点,我应该提到 -a 是必需的参数,这让我们能够识别集合边界。我想简单地为每个参数使用列表并将它们压缩为元组,但是当缺少一些可选参数时,这是不正确的。

标签: python argparse


【解决方案1】:

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

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'])

不幸的是,它确实丢失了一些信息。没有办法将“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 设为必需,因此从其中一个组中省略它会引发错误。


或者适配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

产生:

[{'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')]

【讨论】:

  • 太好了,谢谢,接受 hpaulj 的解决方案,因为问题是 argparse 特定的。尽管 farzad 提供了有用的 cmets 和代码。谢谢你们俩
【解决方案2】:

我们可以遍历命令行参数并将它们一个接一个地添加到集合中,如果我们到达标记另一个集合开始的“-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')

【讨论】:

    【解决方案3】:

    谢谢@hpaulj 和@farzad - 我发现你的回答很有帮助。实现farzad的迭代器的另一种方式:

    more_itertools.split_before(arg_list, lambda arg: arg == '-a')
    

    【讨论】:

      猜你喜欢
      • 2017-08-11
      • 1970-01-01
      • 2017-01-14
      • 2021-09-11
      • 1970-01-01
      • 2016-06-30
      • 1970-01-01
      • 2018-11-23
      • 1970-01-01
      相关资源
      最近更新 更多