python-使用argparse指定文件扩展名

提问

我想用argparse指定几个文件扩展名.

我已经尝试过下面的代码,但是它不起作用.如何使用argparse指定多个文件扩展名?

parser.add_argument('file', action = 'store', type = argparse.FileType('r'), choices=["*.aaa", "*.bbb"])

编辑:我发现我自己的解决方案使用字符串类型而不是FileType:

def input_ok(string):
    if not os.path.exists(string):
        raise argparse.ArgumentTypeError("Filename %r doesn\'t exists in this directory." % string)

    if string[-4:] != ".aaa" and string[-4:] != ".bbb":
        raise argparse.ArgumentTypeError("%r is not a .aaa or a .bbb file." % string)
return string

parser.add_argument('input_path', action = 'store',
    type = input_ok, #argparse.FileType('r'), #choices=["*.stl", "*.csv"])

最佳答案

问题的症结在于choices如何工作. Argparse首先构建一个传递参数的列表并进行类型转换,然后使用in运算符检查是否包含在选择中.这意味着它将不执行任何模式匹配(以匹配’* .aaa’),而是检查字符串是否相等.相反,我们可以使自己的容器传递给选择.

在不使用argparse.Filetype的情况下,它看起来像这样. Argparse还需要容器具有__iter__来使Metavar元组成为帮助消息.

class Choices():
    def __init__(self, *choices):
        self.choices = choices

    def __contains__(self, choice):
        # True if choice ends with one of self.choices
        return any(choice.endswith(c) for c in self.choices)

    def __iter__(self):
        return iter(self.choices)

parser.add_argument('file', action='store', choices=Choices('.aaa', '.bbb'))

您可以通过更改__contains__使其适合您的需求来扩展此思想以执行几乎所有操作.例如,如果还传递了type = argparse.FileType(‘r’),则argparse将在检查包含之前转换为文件对象.

def __contains__(self, choice):
    # choice is now a file object
    return any(choice.name.endswith(c) for c in self.choices)

顺便说一句,这就是为什么我讨厌argparse.它过于复杂,并且尝试做的事情超出了应有的程度.我认为验证不应在参数解析器中完成.使用docopt并自行验证.

评论