Python argparse 详细解释

如果你正在搜索关于 argparse 用法的解释,那么你通常会很困惑,像下面这段代码到底干了什么?解决了什么问题?为什么要用这样的代码?

import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=max,
                    help='sum the integers (default: find the max)')

args = parser.parse_args()
print(args.accumulate(args.integers))

要解释这段代码干了什么,必须先回答什么是 argv,什么是 argc

如果你曾经接触 C 语言,那么你可能会见过下面的一段代码:

#include <stdio.h>
int main(int argc, char * argv[]) {
	for (int i = 0; i < argc; i++) {
		printf("argv[%d]: %s\n", i, argv[i]);
	}
	return 0;
}

它的运行效果是这样的:

$ gcc a.c
$ ./a.out hello world
argv[0]: ./a.out
argv[1]: hello
argv[2]: world

这又是啥?

这个问题其实很简单,有一些程序所需要的数据或者条件并不能在编写代码时就确定。打一个比方,假设你正在编写一个 Email 客户端,但是你并不知道用户要发送邮件给谁,所以在程序运行时必须通过某种途径得到收件人的地址,常见的方法有:

  1. 制作一个图形界面,优雅地问用户收件人是谁
  2. 读取硬盘上的一个文件,比方说 C:\receiver.txt (Windows)或者 /home/user/receiver.txt (Linux/Unix),这要求用户预先把收件人的地址保存到文本文件中
  3. 读取环境变量(对,就是Windows用户熟悉的为 Python 设置 PATH 的界面)
  4. 在运行程序时附加收件人的地址

我相信你一定曾使用过命令行运行过 Python 脚本,例如这样的:

python3 -v some_script.py

在这行命令中,python3 是我们要执行的程序,而它之后的所有内容都是附加给它的信息,即 -v some_script.py 是附加给 Python 程序运行时的信息。其中,-v 告诉 Python 解释器以调试模式运行,some_script.py 则指明了要运行哪个 Python 脚本文件。

这个过程由操作系统来完成,各个语言都有接口来读取附加的信息。上文的 C 程序就是通过 C 语言提供的接口,把收到的信息打印出来。C 语言已经为我们做了一定的预处理,附加信息已经从一整串字符串变成了按照空格划分的字符串列表:argc 代表这个列表有几个元素,argv[i] 代表第 i 个字符串。这些附加的信息被称之为 argument(s)arg 则是它的简写。

历史上 C 语言是第一个大规模流行的跨平台语言,所以之后的编程语言几乎都遵从了它的设计,Python 也是。

比如下面的 Python 代码打印了所有附加的信息:

import sys
for i in range(len(sys.argv)):
	print('argv[' + str(i) + ']:', sys.argv[i])

它的运行效果是这样的:

$ python3 a.py hello world
argv[0]: x.py
argv[1]: hello
argv[2]: world

这样,我们就通过 Python 所提供的接口,获取到了程序运行时用户附加的所有信息。现在我们可以回答最初的那个问题:argparse 有什么用?

试想这样一个问题,如果程序有太多的数据或者条件只能在运行时才能确定,会怎么样?例如一个发送 Email 的程序,需要知道很多的信息:

sendmail --receiver someone@example.com
         --sender anotherone@anotherexample.com
         --force-point-to-point-security
         --max-retry 10
         --retry-interval 120

其中 receiver 代表收件人,sender 代表发件人, force-point-to-point-security 代表一定要使用安全的传输方式,max-retry 代表如果发送失败,则最多尝试的发送次数,retry-interval 代码两次发送尝试之间的间隔(秒)。

当没有 argparse 这个包时,你的代码的开始部分可能充斥着这样的逻辑:检查用户有没有提供所有必要的信息,检查是否是一个合法的邮箱地址,检查尝试次数和尝试间隔是不是数字而非含有英文字母的非法输入。这些检查太累人了,argparse 包就是为了让你从这些没有价值的工作中解放出来而诞生的,你要做的,就是告诉它,哪些参数是必须的,例如这里的 receiversender 是必须的;是字符串还是数字;还是仅仅是一个开关,例如 force-point-to-point-security;顺便给用户一个友好的反馈,在其没有提供合理的信息时,提示他该更正和补充哪些内容。

让我们回到 Python 官方文档给出的例子,其中有四个关键的内容:

  1. parser = argparse.ArgumentParser():新建了一个 Parser 来负责帮助我们处理附加的信息
  2. parser.add_argument():定义了一个所需要输入的参数,包括它的类型、是否可选、名称等等
  3. args = parser.parse_args() 告诉 Parser 我已经定义了所有的参数,可以帮助我进行处理了
  4. args 保存了最终所有的结果,当然了,如果用户输入的信息不对,那程序就报错并终止运行了

对于我们的 sendmail 程序来说,它的 args 可能是这样的:

{'force-point-to-point-security': True,
 'max-retry': 10,
 'receiver': 'someone@example.com',
 'retry-interval': 120,
 'sender': 'anotherone@anotherexample.com'}

这样,我们就可以方便且安全的使用用户输入的数据或条件了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据