Python 使用argparse时,验证和初始化是否应在自定义类型或操作中进行?

Python 使用argparse时,验证和初始化是否应在自定义类型或操作中进行?,python,argparse,Python,Argparse,Python2.7的argparse提供了两个扩展点,您可以在其中控制命令行参数的解析方式:类型函数和操作类 从内置类型和操作开始,最佳实践似乎是类型函数应该包含验证/初始化代码,操作应该关注将值存储到名称空间中。这种方法的问题是,当您有具有副作用的类型检查代码时。考虑这个简单的例子: from argparse import ArgumentParser, FileType argp = ArgumentParser() argp.add_argument('-o', type=FileTyp

Python2.7的argparse提供了两个扩展点,您可以在其中控制命令行参数的解析方式:类型函数和操作类

从内置类型和操作开始,最佳实践似乎是类型函数应该包含验证/初始化代码,操作应该关注将值存储到名称空间中。这种方法的问题是,当您有具有副作用的类型检查代码时。考虑这个简单的例子:

from argparse import ArgumentParser, FileType
argp = ArgumentParser()
argp.add_argument('-o', type=FileType('w'), default='myprog.out')
argp.parse_args(['-o', 'debug.out'])
如果运行此命令,您会发现python将在系统上打开两个文件,
myprog.out
debug.out
。当用户不提供
-o
参数时,只打开
debug.out
更有意义

稍微转一转,argparse似乎只会在传递的参数或str类型的默认参数上调用您的类型函数。如果您的类型检查器有副作用,则这是不幸的,因为即使传递了值,它也会在默认参数上调用。因此,对于具有副作用的初始化,可能最好在操作中执行。问题是,如果您提供默认值,则不会调用该操作

考虑以下代码:

from argparse import ArgumentParser, Action

def mytype(arg):
    print 'checking type for ' + repr(arg)
    return arg

class OutputFileAction(Action):
    def __call__(self, parser, namespace, values, option_string=None):
        print 'running action for ' + repr(values)
        try:
            outstream = open(values, 'w')
        except IOError as e:
            raise ArgumentError('error opening file ' + values)
        setattr(namespace, self.dest, outstream)

argp = ArgumentParser()
argp.add_argument('-o', type=mytype, action=OutputFileAction, default='myprog.out')
现在尝试使用它:

>>> argp.parse_args([])
checking type for 'myprog.out'
Namespace(o='myprog.out')
>>> argp.parse_args(['-o', 'debug.out'])
checking type for 'myprog.out'
checking type for 'debug.out'
running action for 'debug.out'
Namespace(o=<open file 'debug.out', mode 'w' at 0x2b7fced07300>)
>argp.parse_args([])
正在检查“myprog.out”的类型
命名空间(o='myprog.out')
>>>argp.parse_args(['-o',debug.out']))
正在检查“myprog.out”的类型
正在检查“debug.out”的类型
正在运行“debug.out”的操作
名称空间(o=)

是谁下令这样做的?有没有一种合理的方法可以让默认值的行为完全像用户传入的一样?或者在提供值时不检查默认值?

据我所知,没有“合理”的方法来做到这一点。当然,关闭
类型
转换,然后对从
parse_args
返回的
命名空间
进行后处理是很简单的:

args = argp.parse_args()
args.o = open(args.o,'w')

但我想这不是你想要的。

是的,当然,这是有效的。我希望在类型验证器或操作类中执行此操作的原因是,我正在编写一套命令行工具,它们都采用类似的参数,并且我希望将解析、验证和实例化这些参数的代码分解为一组通用函数/类。当在参数定义中指定参数处理例程时,代码会更干净。我认为,如果您不费太多功夫就将ArgumentParser子类化,您可以管理这一点……我想知道,如果行为只是调用类型检查器并仅对应用的参数(传递的字符串)执行操作,会有多少程序中断。但是,如果默认值不是basestring类型,则将跳过类型检查步骤。