Python 如何使用nargs='+';

Python 如何使用nargs='+';,python,python-3.x,parameter-passing,argparse,Python,Python 3.x,Parameter Passing,Argparse,以下是我试图实现的目标的简短总结: 获取一个或多个(表格)文件 寻找一个特定的字符串键(目前只有一个键,但我可以想象将其扩展为多个键) 匹配键的行,提取给定位置或位置范围处的字符 因此,在理想情况下,我希望以以下方式运行我的脚本,它们都是有效的用例: python myscript.py file1 file2 file3 "key" 10 python myscript.py file1 file2 "key" 10 12 python myscript.py file1 "key" 10

以下是我试图实现的目标的简短总结:

  • 获取一个或多个(表格)文件
  • 寻找一个特定的字符串键(目前只有一个键,但我可以想象将其扩展为多个键)
  • 匹配键的行,提取给定位置或位置范围处的字符
因此,在理想情况下,我希望以以下方式运行我的脚本,它们都是有效的用例:

python myscript.py file1 file2 file3 "key" 10
python myscript.py file1 file2 "key" 10 12
python myscript.py file1 "key" 10 12
python myscript.py file1 "key" 10
考虑到我尝试过的用例:

parser = argparse.ArgumentParser()
parser.add_argument("files", nargs='+', help="input files")
parser.add_argument("gene", help="gene of interest")
parser.add_argument("pos", nargs='+', type=int, help="position(s) to analyze")

""" Validate pos """
if len(args.pos) > 2:
    sys.exit("Positions argument needs to be a single integer or two integers denoting a range!")

...

# in some other function
if len(args.pos) == 1:
    key = row[SIND][args.pos[0]]
elif len(args.pos) == 2:
    key = row[SIND][args.pos[0] : args.pos[1]]
else:
    print(args.pos, len(args.pos))
    sys.exit("args.pos assertion failed!")
它在只有一个整数时有效,但在我发送两个整数来分析一个范围时无效。在后一种情况下,“key”也被解释为一个文件,因此我得到
FileNotFoundError:[Errno 2]没有这样的文件或目录:“IGHV4-39”

问题1:是否可以标记或指示位置参数,以便我可以在
文件
参数结束和
基因
启动时告诉argparse?我不想让它们成为可选参数,因为如果忽略这三个参数中的任何一个,脚本的逻辑都是不完整的

问题2:将pos参数一分为二是否有其他帮助;pos取一个整数,range取两个整数,然后使它们以某种方式互斥

有什么想法吗?

回答问题1 “是否可以标记……位置参数?”好吧,如果你这样做了,那么它们就不再是位置参数了!因为所有位置参数都是通过位置而不是标记来定义的

我认为您真正需要的是
required
选项,而不是尝试使用位置参数。您的目标是创建一个非常聪明的界面,无论输入如何,都能神奇地解决您想要的问题,还是创建一个可预测工作并产生良好错误消息的界面

在这一点上我可以讽刺,因为我过去花了太多时间做第一件事,而不仅仅是做第二件事

那么,为什么不:

test.py 因此:

对问题2的答复 你不是在问“这里最好的界面是什么”吗?这当然是你需要决定的。我的建议是:保持简单。为什么不:

parser.add_argument('--start', '-s', required=True, nargs=1, type=int)
parser.add_argument('--end', '-e', nargs=1, type=int)

opts = parser.parse_args()
if opts.end is None:
    opts.end = opts.start + 1

下面是一个
sys.argv
parse的快速实现:

In [97]: txt='file1 file2 file3 "key" 10 12'
In [98]: argv=txt.split()
In [99]: argv
Out[99]: ['file1', 'file2', 'file3', '"key"', '10', '12']
In [104]: files,rows,key=[],[],None
In [105]: for a in argv[::-1]:
     ...:     try:
     ...:         rows.append(int(a))
     ...:     except ValueError:
     ...:         if key is None: key=a
     ...:         else: files.append(a)
     ...:         
In [106]: files
Out[106]: ['file3', 'file2', 'file1']
In [107]: rows
Out[107]: [12, 10]
In [108]: key
Out[108]: '"key"'
您甚至可以使用
parse_known_args
来处理其他标记的参数,并将此逻辑应用于unpasse
extra


argparse
从左到右处理参数;您的位置逻辑更适合相反的情况。标志(
optionals
)在参数字符串之间提供定义良好的分隔符。没有它们,解析多个
+
参数是不可能的。第一个“+”是贪婪的(想想
regex
behavior),它攫取了一切。并且在为参数分配字符串时,它不会检查类型。类型转换(例如,到
int
)在字符串分配后发生。

您可以使用标记的参数(是的,它们可能是必需的),或者自己解析
sys.argv
。您的逻辑似乎倾向于从末尾解析该列表:例如,去掉尾随的整数、键字符串和其他文件名。问题是您建议自己解析参数,而不是学习如何正确使用argparse。argparse功能非常强大,因此,即使它不能完全满足OP的要求,但从长远来看,最好的建议是让他们稍微调整一下,以适应argparse。总的来说,这是一个很好的建议:要适应一个使用良好的框架,不要自己动手。更不用说测试了
argparse
是一种工具,而不是规则。
parser.add_argument('--start', '-s', required=True, nargs=1, type=int)
parser.add_argument('--end', '-e', nargs=1, type=int)

opts = parser.parse_args()
if opts.end is None:
    opts.end = opts.start + 1
In [97]: txt='file1 file2 file3 "key" 10 12'
In [98]: argv=txt.split()
In [99]: argv
Out[99]: ['file1', 'file2', 'file3', '"key"', '10', '12']
In [104]: files,rows,key=[],[],None
In [105]: for a in argv[::-1]:
     ...:     try:
     ...:         rows.append(int(a))
     ...:     except ValueError:
     ...:         if key is None: key=a
     ...:         else: files.append(a)
     ...:         
In [106]: files
Out[106]: ['file3', 'file2', 'file1']
In [107]: rows
Out[107]: [12, 10]
In [108]: key
Out[108]: '"key"'