如何处理与argparse有关系的命令行参数?

时间:2017-09-15 03:10:57

标签: python argparse

假设我有一个名为myprog的程序,它接受一些文件名作为输入,我还想使用命令行参数来设置每个文件的打开模式。 例如

myprog --input a.txt --mode r --input b.txt --input c.txt --mode a

这意味着使用模式a.txt打开文件r,文件b.txt没有--mode arg,因此请使用默认模式r打开它,对于文件c.txt,请使用a模式将其打开。

2 个答案:

答案 0 :(得分:4)

这是一个棘手的问题,因为argparse不会让您知道哪个--input--mode相关联。您可以更改命令的结构,以便文件名和模式由一个标记字符分隔:

myprog --input a.txt:r --input b.txt --input c.txt:a

显然,这假设您没有名称以:<mode>结尾的文件,其中<mode>是任何可接受的文件模式。如果这是一个OK结构,那么这就像编写自定义动作或类型来解析字符串并返回合适的对象一样简单。 e.g。

def parse_fstr(s):
    filename, _, mode = s.rpartition(':')
    return (filename, mode or 'r')

其他解决方案可能涉及使用nargs='*'然后解析传递的参数列表。

最后,为了实现您实际要求的内容而没有太多困难,我们需要做出一个假设。假设argparse将从左到右解析项目。鉴于库的功能,就我所知,这是实现的唯一合理选择......

鉴于该实现,我们可以使用自定义类型和自定义Action执行此操作。该类型只是一种将filenamemode组合在一起的结构。我们每次点击argparse时都会使用--input构建此类型的新实例,并将其附加到列表中(argparse开箱即用)。接下来,我们将编写一个自定义操作,以便在每次输入mode参数时更新列表中最后一个“文件结构”的--mode

import argparse


class FileInfo(object):
    def __init__(self, name, mode='r'):
        self.name = name
        self.mode = mode

    def __repr__(self):
        return 'FileInfo(name={!r}, mode={!r})'.format(self.name, self.mode)


class UpdateMode(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        try:
            last_file_info = namespace.input[-1]
        except IndexError:
            # No file-info added yet.  Error.
            parser.error('{} must come after an --input'.format(option_string or '--mode'))

        last_file_info.mode = values


parser = argparse.ArgumentParser()
parser.add_argument('--input', action='append', type=FileInfo)
parser.add_argument('--mode', action=UpdateMode)
print(parser.parse_args())

如果--mode出现在任何--input之前,我选择抛出错误,但如果2 --mode跟随--input,我只是覆盖了之前的错误值。如果你想进行更多的错误检查,那么在FileInfo类中编写更多代码就可以确保在更新模式时没有设置任何模式。

答案 1 :(得分:0)

如果命令行是这样的:

myprog --input a.txt --mode r --input c.txt --mode a --input b.txt

可以添加如下代码:

import argparse

parser = argparser.ArgumentParser()
parser.add_argument('--input', action='append')
parser.add_argument('--mode', action='append')
args = parser.parse_args()
args_dict = vars(args)

然后你可以解析args对象, args_dict 变量。价值是这样的:

$ python test.py --input test.txt --mode w --input test3.txt --input test2.txt --mode a
{'mode': ['w', 'a'], 'input': ['test.txt', 'test3.txt', 'test2.txt']}

您可以在 args_dict 中迭代&quot;&#39; 键和&#39; 键变量,对于输入列表的剩余(它的&#39; test2.txt&#39;这里),你可以用&#39; r&#39;模式。

但是如果你的命令行必须写出类似的东西:

myprog --input a.txt --mode r --input b.txt --input c.txt --mode a

我不认为使用&#39; r&#39;解析b.txt很容易。模式,因为argparse不知道哪个模式绑定到相对输入...

从@mgilson的评论和回答中获取灵感,我找到了另一种定义Action子类的方法,并使&#39; 输入有用。

class ExtendReadOnlyAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        inputs = namespace.input
        modes = getattr(namespace, self.dest)
        if modes is None:
            modes = []
        modes.extend(['r' for i in range(len(inputs) - len(modes))])
        modes[-1] = values
        setattr(namespace, self.dest, modes)

客户端代码可以是这样的:

import argparse

parser = argparser.ArgumentParser()
parser.add_argument('--input', action='append')
parser.add_argument('--mode', action=ExtendReadOnlyAction)
args = parser.parse_args()
args_dict = vars(args)

然后我们可以更轻松地解析args对象, args_dict 变量。如果命令行是这样的:

$ python test.py --input test.txt --mode w --input test2.txt --input test3.txt --mode a

结果将是:

{'mode': ['w', 'r', 'a'], 'input': ['test.txt', 'test2.txt', 'test3.txt']}

以另一种特殊方式,如果命令行是这样的:

$ python test.py --input test.txt --mode w --input test2.txt --input test3.txt --input test4.txt

结果将是:

{'input': ['test.txt', 'test2.txt', 'test3.txt', 'test4.txt'], 'mode': ['w']}

然后你可以更容易地解析字典,&#39; test2.txt~test4.txt&#39;在输入参数中将有默认的&#39; r&#39;模式:)