如何在argparse

时间:2018-04-08 03:25:56

标签: python-3.x argparse

我正在处理一个(Python 3.x)脚本(由其他人编写),其中输入和输出当前使用标记的可选参数指定,如下所示:

parser.add_argument('-i', '--input', nargs='?', type = argparse.FileType('r'),
                    default=sys.stdin, dest='inputfile')
parser.add_argument('-o', '--output-file', nargs='?', type=argparse.FileType('w'),
                    default=sys.stdout, dest='outputfile')

我想升级此脚本,以便可以将输入和输出文件指定为位置参数,同时保持现有的标志参数以实现向后兼容。我还想智能地处理将标记的参数与位置参数混合可能产生的潜在冲突(即,如果只给出-i-o中的一个,那么单个位置参数是自动传递给另一个,两个位置参数引发冗余错误,而如果同时给出-i-o,则任何位置参数都会引发冗余错误。)

注意:当前编写的脚本不接受任何位置参数,尽管它接受其他标志,其中一些标题没有,除了与输入和输出文件相关的标志之外。

这可能与argparse(如果是这样,如何)或我是否必须使用其他东西重写参数解析(如果是这样,你有什么建议)?

2 个答案:

答案 0 :(得分:1)

坚持使用FileType会很尴尬。 type打开或创建文件。因此,当您只需要2时,您可能会有4个打开的文件。但如果其中一个文件是stdinout,则您不想关闭它。而且你不能处理一个positional,它可以是读或写,取决于给出的其他参数。

您可以尝试定义4个默认字符串参数,2个标记位置和2个nargs='?'位置。给他们不同的dest。然后,您可以将您的智能应用于4个可能的值。默认默认值None应足够清楚,表明未提供值。一旦您确定了两个文件名,就可以打开并使用它们。较新的Python建议使用with上下文,但是当文件已经打开时(例如sys.stdin),这可能会很尴尬。

我认为您不应该尝试在argparse内实施该逻辑。解析后再做。

答案 1 :(得分:1)

对于那些对更明确的答案感兴趣的人,以下是我最终实现@ hpaulj的建议:

首先,我为输入和输出文件参数定义了一个参数组:

files = parser.add_argument_group('file arguments:',description='These arguments can also be provided as postional arguments, in which case the input file comes first.')
files.add_argument('-i', '--input', nargs='?',
                    help='Source of the words to be syllabified.  If None or -, then input will be read from stdin.',
                    dest='inputfile')
files.add_argument('-o', '--output-file', nargs='?',
                    help='Destination of the syllabified words.  If None or -, then ouput will be written to stdout.',
                    dest='outputfile')
files.add_argument('fileone',nargs='?',
                     help=argparse.SUPPRESS)
files.add_argument('filetwo',nargs='?',
                     help=argparse.SUPPRESS)

这使我能够将参数保持在一起,与程序的其他参数分开,并提供更多控制,帮助文本将如何显示,以便最有意义。

然后,在解析参数(args = parser.parse_args())之后,我添加了以下逻辑来确定正确的输入和输出是什么,并在适当时打开文件或stdin和stdout:

if (args.inputfile == None or args.inputfile == '-'):
    if (args.outputfile == None or args.outputfile == '-'):
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = sys.stdin
            output = sys.stdout
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            try:
                input = open(args.fileone,'r')
                output = sys.stdout
            except:
                input = sys.stdin
                output = open(args.fileone,'w')
        else:
            input = open(args.fileone,'r')
            output = open(args.filetwo,'w')
    else:
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = sys.stdin
            output = open(args.outputfile,'w')
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.fileone,'r')
            output = open(args.outputfile,'w')
        else:
            print("Error: too many files")
            print("Both -o and positional output file given")
            sys.exit(1)
else:
    if (args.outputfile == None or args.outputfile == '-'):
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = sys.stdout
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = open(args.fileone,'w')
        else:
            print("Error: too many files")
            print("Both -i and positional input file give")
            sys.exit(1)
    else:
        if (args.fileone == None or args.fileone == '-') and (args.filetwo == None or args.filetwo == '-'):
            input = open(args.inputfile,'r')
            output = open(args.outputfile,'w')
        elif args.fileone != None and (args.filetwo == None or args.filetwo == '-'):
            print("Error: too many files")
            print("Both -i and -o given with a positional file")
            sys.exit(1)
        else:
            print("Error: too many files")
            print("Both -i and -o given with positional files")
            sys.exit(1)

正如您所看到的,我决定接受默认的None-作为引用stdin / stdout的可能性。这复制了FileType参数的行为,该参数也以这种方式接受-

剩下的一个歧义是"None"(即一个单词" None")与None(即NoneType类)和帮助不同消息可能被解释为暗示-i None应该引用stdin。但是,我认为这里的差异对于大多数python用户来说应该是显而易见的,我不会为了这种可能性而进一步复杂化逻辑。