跳至主要内容

Python 中的参数解析

在本教程中,学习如何使用 getopt、sys 和 argparse 模块从命令行或终端解析一个或多个参数。
· 10分钟 阅读

使用 AI 探索

如果您计划开展数据科学或机器学习项目,从Jupyter Notebook 开始开发并不罕见。它为您的计算工具提供交互性,让您可以快速运行模块等等。选择 Jupyter Notebook 作为主要工具有很多理由,特别是在进行数据科学或机器学习项目时。不过,本教程并不打算讨论这一部分。

设想您需要将一个 Python 脚本作为批处理作业来运行,以在数据科学项目中进行数据摄取;或者您需要运行一个 Python 脚本,将机器学习模型部署到远程服务器。在这些情况下,仅仅执行 python your_script.py 可能不够。您可能需要在 python your_script.py 之后再传入更多选项或参数。您或许已经熟悉在 Linux 终端运行命令(如 ps、ls 等)时使用附加参数的用法。

在本教程中,您将学习如何解析与 Python 脚本一起从命令行传入的其他参数和选项。以下是本教程的内容摘要:

  • 参数解析的基本介绍
  • Python 中的参数解析
    • 使用 sys.argv
    • 使用 getopt
    • 使用 argparse

注意:本教程假设您已具备 Python 的基础知识。

什么是参数解析?

如果您是 Linux 用户,您很可能已经了解了。我敢打赌。为了获取某个目录下文件和文件夹的摘要,通常会使用 ls 命令。运行 ls 命令的典型输出如下所示——

command

不过,您可以为 ls 命令提供许多选项,例如 -l。我们来看看它的输出——

output

通过传入 -l 这个选项,您获得了更多信息。现在,在这个语境中,选项和参数之间有细微差别。要从当前工作目录中删除某个文件或文件夹,通常使用 rm 命令。假设您有一个名为 demo.txt 的文本文件,并运行 rm demo.txt 以删除该文件。在这种情况下,demo.txt 是您传给 rm 命令的参数。如果您不给 rm 命令传入任何参数,您会得到如下错误——

error

那么,选项和参数的区别是什么?现在已经很明显了。选项是可选的,而参数往往是必需的。ls 命令不需要传入任何内容也能产生输出。

当您输入 ls -l 或 rm demo.txt 时,操作系统会在底层以特定方式对其进行解析。该机制通常由操作系统的开发者指定,称为解析。

如果您有兴趣了解更多 Linux 命令以及它们支持的不同选项和参数,强烈推荐阅读《Unix Concepts and Applications》一书。

以上是参数解析的基本介绍。几乎所有编程语言都支持参数解析,Python 也不例外。在 Python 中,常见的参数解析方式有三种——

  • sys.argv
  • getopt
  • argparse

接下来我们将逐一介绍上述选项,并了解如何用它们来解析参数(和选项)。让我们按时间顺序来。

使用 sys.argv 进行参数解析

我们从简单开始。在第一个示例中,您将创建一个名为 demo1.py 的 Python 文件。程序在执行时会接受从命令行(或终端)传入的任意数量的参数。程序将打印传入的参数以及参数总数。

例如,如果您执行 python demo1.py abc 123,程序将输出——

Number of arguments: 3
Argument(s) passed: ['demo1.py', 'abc', '123']

请注意,第一个参数始终是Python 文件的名称。当然,代码可以修改为不包含它。

下面来看代码——

import sys

print('Number of arguments: {}'.format(len(sys.argv)))
print('Argument(s) passed: {}'.format(str(sys.argv)))

您首先导入了 Python 模块 sys,它随标准的 Python 安装提供。然后使用了 argv 子模块,它会返回传给 Python 脚本的参数列表,其中 argv[0] 包含 Python 脚本的名称。如果您像下面这样在代码单元中运行 sys.argv,您会得到一组使 IPython 内核正常运行的配置文件列表。最好不要去动它们。

sys.argv
['/home/nbuser/anaconda3_501/lib/python3.6/site-packages/ipykernel/__main__.py',
 '-f',
 '/home/nbuser/.local/share/jupyter/runtime/kernel-dabba4f7-e3e0-4c39-99d2-261ba835c53f.json']

现在我们来看看如何使用 getopt 模块来解析参数(和选项)。

使用 getopt 进行参数解析

与 sys.argv 相比,getopt 模块提供了更强的灵活性。我们先设计一个示例场景,并据此编写代码。

假设您发布了一个尚未编写文档的 Python 脚本,但希望在用户以错误方式执行脚本时,仍能给出有意义的反馈。您的 Python 脚本做的只是将两个数字相加并输出结果。唯一的约束是,用户需要以命令行参数的形式与 Python 脚本一起传入输入。

更实际一点地说,脚本理想的执行方式是——

python add_numbers.py -a 3 -b 8

输出应为 11。这里,-a 和 -b 是选项,3 和 8 是传给脚本的参数。选项不仅提升可读性,还能帮助决定运算流程(想象一下如果您不是做加法而是做除法)。我们先来看代码。

import getopt
import sys

# Get the arguments from the command-line except the filename
argv = sys.argv[1:]
sum = 0

try:
    # Define the getopt parameters
    opts, args = getopt.getopt(argv, 'a:b:', ['foperand', 'soperand'])
    # Check if the options' length is 2 (can be enhanced)
    if len(opts) == 0 and len(opts) > 2:
      print ('usage: add.py -a <first_operand> -b <second_operand>')
    else:
      # Iterate the options and get the corresponding values
      for opt, arg in opts:
         sum += int(arg)
      print('Sum is {}'.format(sum))

except getopt.GetoptError:
    # Print something useful
    print ('usage: add.py -a <first_operand> -b <second_operand>')
    sys.exit(2)

思路是先用 sys.argv 获取所有参数,然后按需处理。现在来看最重要的一行代码——opts, args = getopt.getopt(argv, 'a:b:', ['foperand', 'soperand'])

getopt() 方法的签名如下:

getopt.getopt(args, shortopts, longopts=[])

  • args 是从命令行获取的参数列表。
  • shortopts 用于指定选项字母。如果您提供 a:,就表示脚本应接收选项 a,后跟一个值作为其参数。从技术上讲,您可以在此处使用任意数量的选项。在命令行传入这些选项时,必须以 '-' 作为前缀。
  • longopts 用于指定 shortopts 的扩展版本。它们必须以 '--' 作为前缀。

您将 shortopts 定义为 a:b:,这意味着 Python 脚本将接收两个字母选项——“a” 和 “b”。通过指定 “:”,您明确告知这些选项后面会跟随参数。

您将 longopts 定义为 ['foperand', 'soperand'],这表示代码可以接受 f_operand 和 s_operand 来替代 a 和 b 作为选项。但这些应始终以 -- 作为前缀。

getopt 模块还为您提供了一个方便的异常类 GetoptError,可用于定义有用的信息,从而指导用户如何使用您的 Python 脚本。这也是您将脚本的功能部分放入 try 块,并相应定义 except 块的原因。

以下是运行该 Python 脚本的方式:

python script

注意:上述脚本未定义对 longopts 的处理。这一点可以留作练习。

现在来看如何使用 argparse 进行参数解析。

使用 argparse 进行参数解析

从上面的两种方式可以看出,它们并不是很易读。当您看到 argparse 的实际用法时,也会同意前两种方式在灵活性方面有所欠缺。为理解 argparse 的用法,我们从一个代码片段开始,它实现了上一节所看到的场景。

import argparse

# Construct the argument parser
ap = argparse.ArgumentParser()

# Add the arguments to the parser
ap.add_argument("-a", "--foperand", required=True,
   help="first operand")
ap.add_argument("-b", "--soperand", required=True,
   help="second operand")
args = vars(ap.parse_args())

# Calculate the sum
print("Sum is {}".format(int(args['foperand']) + int(args['soperand'])))

首先,和上面讨论的另外两个模块一样,argparse 也随 Python 的标准安装提供。您先实例化一个 argparse 对象。之后的事情写起来就简单多了。以如下代码为例——

ap.add_argument("-a", "--foperand", required=True, help="first operand")

这里您添加了在运行 Python 脚本时期望提供的参数。您同时提供了该参数的字母版本和扩展版本。通过指定 required=True,您明确要求用户必须提供该参数。最后,您附加了一段有意义的参数说明,如果用户没有以正确方式执行脚本,这段说明将显示给用户。

第二个参数的定义部分也是相同的。您可以通过指定相应的键来访问给定的参数。

上述代码片段清楚地展示了使用 argparse 定义命令行参数解析任务是多么简洁灵活。以下是您可以如何尝试运行上述 Python 脚本:

python script

注意使用 argparse 后流程变得多么简单。

下一步是什么?

感谢您读到本教程的最后。现在,您已经掌握了 Python 自带的、能够解析与脚本一同从命令行传入参数的模块。作为练习,您可以据此自定义自己的机器学习和数据科学项目,以便进行参数解析并将其变成实用的脚本工具。不仅是机器学习或数据科学,只要您觉得合适的地方都可以。这就是今天教程的全部内容。如果您希望提升 Python 技能,可以考虑学习 DataCamp 的 Python Data Science Toolbox(第 1 部分) 课程。

主题