Argparse:使用nargs='+'的两个位置参数

6

我正在尝试对图像进行数学运算。 我已定义以下内容(这是我的真实代码的简化版本):

parser = argparse.ArgumentParser(description='Arithmetic operations on images')
parser.add_argument("input1", metavar='input1', action='store',
      help='list of input images to operate with', nargs="+", type=str)
parser.add_argument("operation", metavar='operation', action='store', type=str, 
      help='type of operation (+,-,*,/) to be done', nargs=1)
parser.add_argument("input2",metavar='input2', action='store', nargs="+", 
      type=str, help='image (or value) with which to perform the operation on input1')

这段代码会产生:
arith.py -h
usage: arith.py [-h] input1 [input1 ...] operation input2 [input2 ...]

所以它确实理解输入1可能包含一个或多个元素,操作将是单一的,输入2可以是任意数量的元素。
问题在于,有两个位置参数和一个未确定元素数量的元素,argparse会混淆哪个是哪个。我已经尝试向'operation'添加choices=["+", "-", "*", "/"],以便它知道在哪里进行分离,但似乎argparse无法实现。实际上,在argparse文档中,关于nargs='*',您可以阅读到:
注意,通常没有必要使用nargs='*'具有多个位置参数
我想我可以将args.input1、args.operation和args.input2相加,并寻找“+”、“-”、“/”和“*”来分离自己,但在做出如此丑陋的事情之前,我想借鉴集体思想。

2
我不确定 argparse 是否适合你在这里所做的事情。 直接通过 sys.argv 进行可能会更容易些。 - khelwood
嗨khelwood。实际上,我使用argparse解析sys.argv。这为用户提供了“免费”的帮助,并且它会为我执行所有检查类型和现有变量的工作。直接使用sys.argv就像我的解决方案一样,将input1、operation和input2汇集在一起,然后自己分离。虽然可行,但很丑陋;)。无论如何,谢谢! - Jblasco
argparse 可以构建这样的帮助信息,但这并不意味着它可以按照您想要的方式解析参数。如果我调用 arith.py a b c d e f g,那么哪一个是 operation?它可能是 bf 中的任何一个。argparse 没有任何回溯模式匹配器内置;它只是以贪婪的方式从左到右处理参数。 - chepner
可能有一种更清晰的方式来构造参数。input1input2是如何处理的?input1的第k个元素是否与input2的第k个元素使用指定的运算符配对?还是计算两个列表的笛卡尔积,并将运算符应用于产品的每个元素?或者是其他什么? - chepner
如果 len(input1) = len(input2),它们会成对配对。如果 len(input2) = 1,则将该单个元素应用于 input1 的所有元素。 - Jblasco
2个回答

2

在为位置参数分配字符串时,解析器仅区分以前缀字符(例如“ - ”)开头的字符串和其余字符串。它无法区分表示“数字”的字符串和表示“操作”的字符串。实际上,它执行以下正则表达式操作:

re.match('(A+)(A)(A+)','AAAAAAAAA')

这将生成(AAAAAA),(A),(A)。它分配足够的字符串来满足最后2组的规格,并将其余的分配给第一组。

因此,您需要某种“标志”来标记第一个列表的结尾。

我认为,这是使用argparse最接近的方法:

parser.add_argument("input1", nargs="+", type=int)
parser.add_argument("-o", "--operation", choices=['+','minus','*','/'] )
parser.add_argument("input2", nargs="+", type=int)

应该转变为

PROG 1 3 4 -o + 5 6 7
PROG 1 3 4 -o+ 5 6 7
PROG 1 3 4 --operation=+ 5 6 7

into(我认为)

进入
namespace(input1=[1,3,4], operation='+', input2=[5,6,7])

请注意,选项列表中不包括“-”。这是因为解析器将其视为前缀字符。可能有一种方法将其作为参数值悄悄加入其中,但我没有时间找出来。
我在解析器中将“input1”值转换为整数。你也可以先进行此操作,然后再将它们转换为浮点数。
我省略了默认参数,例如“type=str”和“action='store'”。
但也许更好的解决方案是将所有值作为1个列表,并自行分割它们。至少对于这3个参数,您并没有充分利用argparse的功能。
alist = ['1','2','3','+','4','5','6']
i = <find index of '+-/*'>
input1 = alist[:i]
operations = alist[i]
input2 = alsits[i+1:]

感谢您的努力。过去,我能够将“-”偷偷地放入非定位参数的唯一方法是:-o " -"因此,在字符串之前加上一个空格,以便“-”被视为字符串的一部分。 - Jblasco
指定其他字符,例如 # 作为前缀字符,可以释放 - 以便用作参数。 - hpaulj
是的,hpaulj,这就是我采取的方法,正如我在原始帖子中暗示的那样。看来我要求太多了。不过,为了用户的清晰度,我只保留了这三个参数。解析后,我再次将它们连接起来并正确分割。 - Jblasco

0
如果你使用了"required optional"参数,你可以让这个工作起来。当从左操作数切换到操作时,需要在命令行上显示选项,以便切换到右操作数。例如。
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument("--left-operands", "-l", nargs="+", required=True)
parser.add_argument("--right-operands", "-r", nargs="+", required=True)
parser.add_argument("--operation", "-o", choices=["+", "-", "*", "/"], required=True)

argline = "-l 1 2 -o + -r 3 4"

print(parser.parse_args(argline.split(" ")))

谢谢,但不完全是我想要的。我希望参数是位置的。否则,我会回到将input1、input2和operation放在一起并在内部分离它们的想法。 - Jblasco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接