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

问题描述:

假设我有一个名为myprog的程序,它将一些文件名作为输入,并且我还希望使用命令行参数为每个文件设置打开模式。 例如如何处理与argparse有关系的命令行参数?

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

这意味着打开文件a.txt与模式r,文件b.txt没有--mode阿根廷,所以用默认模式r打开它,该文件c.txt,使用a模式打开它。

+0

如果您使用的是pycharm,则可以使用设置指定命令行参数。 –

+0

那么,问题是什么? –

+0

你保证每个'--input'都有对应的'--mode'吗?如果是这样,那么你可以在'input'和'mode'上使用'append''动作,然后''压缩'列表。如果没有(根据你的例子看起来就像是这种情况),那么事情就会变得更加困难,除非你愿意重组命令行 - 例如'myprog --input a.text r --input b.txt --input c.txt a ...' – mgilson

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

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

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

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

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


最后,实现你所实际上要求不会有太大困难,我们需要做一个假设。假设​​将从左到右解析项目。鉴于图书馆的功能,这是唯一合理的选择实施,据我所知...

鉴于实施,我们可以做到这一点与自定义类型和自定义Action。该类型只是一个结构,可以将filenamemode分组在一起。每当我们点击一​​个--input并将它追加到一个列表中(这是支持开箱的​​),我们将使用​​构造这种类型的新实例。接下来,我们将编写一个自定义操作,以在每次插入--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类中编写更多代码以确保在您更新模式时尚未设置任何模式。

+0

是的,''parse_args'遍历'argv'('sys.argv [1:]'),或者处理定位和选择权。各自的'nargs'控制给每个'Action'分配多少个字符串。所以你的自定义Action类应该像广告一样工作。 – hpaulj

如果在命令行是这样的:

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_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变量键,为保持输入列表中(它的“的test2.txt”这里),你可以用'r'模式打开它。

但是,如果您的命令行必须写类似:

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

我不认为这是容易解析带“R”模式下的b.txt,因为argparse不知道哪个模式结合相对输入...


获得从@mgilson灵感的意见和答案,我已经找到了另一种方式来定义Action子类,使‘模式’输入有用。

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_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']} 

而且n您可以更轻松地解析dict,输入参数中的'test2.txt〜test4.txt'将具有默认'r'模式:)

+0

这也是我的第一个想法,但考虑一点之后,这绝对是可能的(甚至令人惊讶的是,甚至没有那么难)。由于'argparse'提供了编写自己的动作的钩子,因此可以通过自定义动作对状态机进行编码,而没有太多困难。看到我的回答:-) – mgilson

+0

@mgilson非常感谢您的评论:)解决这个问题显然是很好的一点。但它使得parse_args的*'mode'*值无用。但我认为这不是一个很大的问题。 – Ballack