【问题标题】:How to parse several positional arguments with nargs='+'如何使用 nargs='+' 解析几个位置参数
【发布时间】:2017-03-31 18:41:11
【问题描述】:

以下是我想要实现的目标的简短摘要:

  • 获取一个或多个(表格)文件
  • 查找特定的字符串键(目前只有一个键,但我可以想象将其扩展为包含多个键)
  • 与键匹配的行,提取给定位置或位置范围的字符。

因此,在理想情况下,我希望通过以下方式运行我的脚本,它们都是有效的用例:

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!")

它在只有一个整数时有效,但在我发送两个整数来分析范围时无效。在后一种情况下,“密钥”也被解释为文件,因此我得到FileNotFoundError: [Errno 2] No such file or directory: 'IGHV4-39'

问题 1: 是否可以标记或指示位置参数,以便我可以告诉 argparse files 参数何时结束以及 gene 何时开始?我不想让它们成为可选参数,因为如果省略了三个参数中的任何一个,脚本的逻辑就是不完整的。

问题2: 将 pos 参数一分为二是否有帮助? pos 接受一个整数, range 接受两个整数,然后让它们以某种方式排他?

有什么想法吗?

【问题讨论】:

  • 要么使用标记参数(是的,它们可能是必需的),要么你自己解析sys.argv。您的逻辑似乎倾向于从最后解析该列表:例如拉出尾随整数,键字符串,其余是文件名。

标签: python python-3.x parameter-passing argparse


【解决方案1】:

问题 1 的答案

“是否可以标记...位置参数?”好吧,如果你这样做了,那么它们就不再是位置参数了!因为毕竟位置参数是通过处于位置而不是被标记来定义的。

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

在这一点上我可以讽刺一下,因为过去我花了太多时间做第一个而不是只做第二个。

为什么不这样:

test.py

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('--files', required=True, nargs='+')
parser.add_argument('--genes', required=True, nargs='+')
parser.add_argument(
    '--pos', required=True, nargs='+', type=int, help="position(s) to analyze")

opts = parser.parse_args()
print(dir(parser))

print('files: %s, genes: %s, poses: %s' % (
    opts.files, opts.genes, opts.pos))

所以:

$ python test.py
usage: test.py [-h] --files FILES [FILES ...] --genes GENES [GENES ...] --pos
           POS [POS ...]
test.py: error: the following arguments are required: --files, --genes, --pos

$ python test.py --files file1 file2 --genes xzy --pos 2 1
files: ['file1', 'file2'], genes: ['xzy'], poses: [2, 1]

问题 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

【讨论】:

    【解决方案2】:

    这是sys.argv 解析的快速实现:

    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 来处理其他标记的参数,并将此逻辑应用于 unparse extras

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

    【讨论】:

    • 问题在于您建议自己解析参数,而不是学习如何正确使用 argparse。 argparse 非常强大,因此即使它不能完全满足 OP 的要求,从长远来看,IMO 最好建议他们稍微调整他们想要适应 argparse 的内容。这只是一个很好的建议:适合一个使用良好的框架,不要自己动手。少得多的测试!
    • argparse 是工具,而不是规则。
    猜你喜欢
    • 2015-07-25
    • 1970-01-01
    • 2016-07-19
    • 2017-02-23
    • 1970-01-01
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 2021-02-18
    相关资源
    最近更新 更多