使用argparse指定默认文件名,但在--help时不打开它们?

13 投票
4 回答
9673 浏览
提问于 2025-04-17 06:53

假设我有一个脚本,它会对一个文件进行一些操作。这个脚本可以在命令行中接收文件名,如果没有提供文件名,它会默认使用一个已知的文件名(比如说content.txt)。我使用Python的argparse模块来实现这个功能:

parser = argparse.ArgumentParser(description='my illustrative example')
parser.add_argument('--content', metavar='file', 
                     default='content.txt', type=argparse.FileType('r'),
                     help='file to process (defaults to content.txt)')
args = parser.parse_args()
# do some work on args.content, which is a file-like object

这个方法很好用。唯一的问题是,如果我运行python myscript --help,如果文件不存在,就会出现一个ArgumentError错误(我想这也算合理),而帮助信息就不会显示出来。我希望在用户只想要--help的时候,不要去尝试打开文件。有没有办法做到这一点?我知道我可以把这个参数设置为字符串,然后自己处理打开文件的事情(我一直都是这么做的),但如果能让argparse来处理这个就方便多了。

4 个回答

2

将标准输入作为默认输入:

parser.add_argument('file', default='-', nargs='?', type=argparse.FileType('r'))
13

你可以通过创建一个新的类来扩展 argparse.FileType

import argparse
import warnings

class ForgivingFileType(argparse.FileType):
    def __call__(self, string):
        try:
            super(ForgivingFileType,self).__call__(string)
        except IOError as err:
            warnings.warn(err)

parser = argparse.ArgumentParser(description='my illustrative example')
parser.add_argument('--content', metavar='file', 
                     default='content.txt', type=ForgivingFileType('r'),
                     help='file to process (defaults to content.txt)')
args = parser.parse_args()

这样做就不需要修改像 ArgumentParser._parse_known_args 这样的私有方法。

10

看一下argparse的代码,我发现:

  • ArgumentParser.parse_args会调用parse_known_args,并确保没有待解析的参数。
  • ArgumentParser.parse_known_args会设置默认值,然后调用ArgumentParser._parse_known_args

所以,解决方法就是直接使用ArgumentParser._parse_known_args来检测-h,然后再像往常一样使用ArgumentParser.parse_args

import sys, argparse
parser = argparse.ArgumentParser(description='my illustrative example', argument_default=argparse.SUPPRESS)
parser.add_argument('--content', metavar='file',
                     default='content.txt', type=argparse.FileType('r'),
                     help='file to process (defaults to content.txt)')
parser._parse_known_args(sys.argv[1:], argparse.Namespace())
args = parser.parse_args()

需要注意的是,ArgumentParser._parse_known_args需要两个参数:来自命令行的参数和命名空间。

当然,我不建议使用这种方法,因为它利用了argparse的内部实现,而这些实现将来可能会改变。不过,我觉得这并不是太复杂,所以如果你认为维护的风险是值得的,还是可以考虑使用。

撰写回答