使用 Python 的 argparse 构建命令行界面 #75
shenweiyan
announced in
1.2-编程
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
命令行应用在普通用户空间中可能并不常见,但它们存在于开发、数据科学、系统管理和许多其他操作中。每个命令行应用都需要一个用户友好的命令行界面 (CLI),以便你可以与应用本身进行交互。在 Python 中,您可以使用标准库中的
argparse
模块创建功能齐全的 CLI。在本文中,你将了解如何:
argparse
创建命令行界面(command-line interfaces);argparse
一些强大的功能深度自定义您的 CLI;若要充分利用本教程,应熟悉 Python 编程,包括面向对象编程、脚本开发和执行以及 Python 包和模块等概念。如果您熟悉与使用命令行或终端相关的一般概念和主题,这也将很有帮助。
了解命令行界面
自从计算机发明以来,人类一直需要并找到与这些机器交互和共享信息的方法。信息交换在人、计算机软件和硬件组件之间流动。其中任意两个元素之间的共享边界通常称为接口(interface)。
在软件开发中,接口是给定软件的特殊部分,它允许计算机系统组件之间的交互。当涉及到人机交互和软件交互时,这个重要的组件被称为用户界面(user interface)。
您会在编程中找到不同类型的用户界面。图形用户界面 (GUI) 可能是当今最常见的。但是,您还可以找到为其用户提供命令行界面 (CLI) 的应用和程序。在本教程中,你将了解 CLI 以及如何在 Python 中创建它们。
命令行界面 (CLI)
命令行界面允许您通过操作系统命令行、终端或控制台与应用程序或程序进行交互。
要了解命令行界面及其工作原理,请考虑此实际示例。假设您有一个名为 sample 包含三个示例文件的目录。如果您使用的是类 Unix 操作系统,例如 Linux 或 macOS,请继续在父目录中打开命令行窗口或终端,然后执行以下命令:
Unix 的
ls
命令列出目标目录中包含的文件和子目录,该目录默认为当前工作目录。上面的命令调用没有显示有关 的内容 sample 的太多信息。它只在屏幕上显示文件名。假设你想要获取有关目录及其内容的更丰富信息,那么你不需要寻找其他程序,因为 ls 命令有一个功能齐全的命令行界面,并且提供了一组有用的选项,可以用来定制命令的行为。
例如,继续执行带有
-l
选项的ls
命令:现在,
ls
命令的输出完全不同了。该命令显示了有关 sample 目录中文件的更多信息,包括权限、所有者、组、日期和大小。它还显示了这些文件在你计算机磁盘上使用的总空间。这种更丰富的输出结果是由于使用了
-l
选项,这是 Unixls
命令行界面的一部分,它启用了详细的输出格式。命令、参数、选项、参数和子命令
在本教程中,您将深入了解命令(commands)及其子命令(subcommands),同时还会学习到命令行参数(command-line arguments)、选项(options)和参数(parameters)的相关知识。因此,建议您将这些术语纳入您的技术词汇库中。
-l
)传递给命令。参考上一节中的示例命令结构:
在这个例子中,您组合了命令行界面(CLI)的以下组件:
现在,让我们来看下面的命令结构,它展示了 Python 包管理器
pip
的命令行界面(CLI):这是一个常见的
pip
命令结构,您可能之前已经见过。它允许您使用requirements.txt
文件来给指定的 Python 项目安装依赖项。在这个例子中,您使用了以下命令行界面(CLI)组件:pip
的子命令(subcommand)名称;install
子命令的选项(option);-r
选项的参数。现在您已经了解了命令行界面(CLI)是什么以及其主要部分或组件有哪些。接下来,是时候学习如何在 Python 中创建自己的 CLI 了。
Python 中的 CLI 入门
Python 附带了一些工具,这些工具可帮助您为程序和应用程序编写命令行界面(CLI)。若您需要快速为小型程序构建一个简洁的 CLI,那么可以利用
sys
模块中的argv
属性。这个属性会自动存储您在命令行中传递给特定程序的参数。使用
sys.argv
构建最小的 CLI以使用
argv
创建最小命令行界面(CLI)为例,假设您需要编写一个小程序,该程序类似于ls
命令,能够列出给定目录下的所有文件。在这种情况下,您可以编写如下代码:该程序通过手动处理命令行提供的参数来实现了一个简单的命令行界面(CLI),这些参数会自动存储在
sys.argv
中。sys.argv
的第一个元素始终是程序名称,第二个元素则是目标目录。由于应用程序不应接受超过一个目标目录,因此args_count
不得超过 2。在检查
sys.argv
的内容后,您创建一个pathlib.Path
对象来存储目标目录的路径。如果该目录不存在,您将通知用户并退出程序。接下来的for
循环将列出目录内容,每行一个条目。如果从命令行运行该脚本,您将得到以下结果:
$ python ls_argv.py sample/ hello.txt lorem.md realpython.md $ python ls_argv.py You must specify the target directory $ python ls_argv.py sample/ other_dir/ One argument expected, got 2 $ python ls_argv.py non_existing/ The target directory doesn't exist
您的程序接受一个目录作为参数,并列出其内容。如果您运行命令时没有提供参数,您将收到一个错误消息。如果您运行命令时指定了超过一个目标目录,您同样会收到一个错误消息。如果尝试运行命令并指定一个不存在的目录,程序将输出另一个错误消息。
虽然您的程序运行正常,但对于更复杂的 CLI 应用程序来说,使用
sys.argv
属性手动解析命令行参数并不是一个可扩展的解决方案。如果您的应用需要接受更多的参数和选项,那么解析sys.argv
将变得复杂且容易出错。您需要一个更好的解决方案,这就是 Python 中的argparse
模块所提供的。使用
argparse
创建 CLI在 Python 中创建 CLI 应用程序的更便捷方法是使用标准库中的
argparse
模块。该模块首次在 Python 3.2 中随 PEP-389 一同发布,是快速创建 Python CLI 应用程序的利器,无需安装如 Typer 或 Click 这样的第三方库。argparse
模块是作为较旧的getopt
和optparse
模块的替代品而发布的,因为它们缺乏一些重要的功能。Python 的
argparse
模块允许您:这些特性使
argparse
成为了一个强大的 CLI 框架,您在创建 CLI 应用程序时可以放心地依赖它。要使用 Python 的argparse
,您需要遵循以下四个简单的步骤:argparse
;ArgumentParser
创建参数解析器(argument parser);.add_argument()
方法向解析器添加参数(arguments)和选项(options);.parse_args()
以获取参数Namespace
。例如,您可以使用
argparse
来改进您的ls_argv.py
脚本。现在,您可以创建一个名为ls.py
的脚本,并编写以下代码:随着
argparse
的引入,您的代码发生了显著的变化。与之前的版本相比,最明显的不同是,用于检查用户提供的参数的条件语句已经消失了。这是因为argparse
会自动为您检查参数的存在性。在这个新的实现中,您首先导入
argparse
并创建一个参数解析器。要创建解析器,您可以使用ArgumentParser
类。接下来,您定义一个名为path
的参数,用于获取用户的目标目录。接下来,您需要调用
.parse_args()
方法来解析输入参数,并获取一个包含所有用户参数的Namespace
对象。请注意,现在args
变量保存了一个Namespace
对象,该对象具有从命令行收集的每个参数所对应的属性。在这个例子中,您只有一个参数,名为
path
。Namespace
对象允许您使用点表示法通过args
来访问path
。其余的代码与第一个实现相同。现在继续从命令行运行这个新脚本:
$ python ls.py sample/ lorem.md realpython.md hello.txt $ python ls.py usage: ls.py [-h] path ls.py: error: the following arguments are required: path $ python ls.py sample/ other_dir/ usage: ls.py [-h] path ls.py: error: unrecognized arguments: other_dir/ $ python ls.py non_existing/ The target directory doesn't exist
第一个命令的输出与您的原始脚本
ls_argv.py
相同。而第二个命令的输出则与ls_argv.py
中的输出大不相同。程序现在会显示一个使用说明消息,并发出错误提示,告诉您必须提供path
参数。在第三个命令中,您传递了两个目标目录,但应用程序并未为此做好准备。因此,它再次显示使用说明消息,并抛出一个错误,告知您潜在的问题。
最后,如果您运行脚本时传递了一个不存在的目录作为参数,那么您会收到一个错误,告知您目标目录不存在,因此程序无法执行其工作。
现在,您可以使用一个新的隐式特性。现在,您的程序接受一个可选的
-h
标志。不妨尝试一下:太棒了,现在您的程序会自动响应
-h
或--help
标志,并为您显示带有使用说明的帮助消息。这真是一个很棒的特性,而且您只需在代码中引入argparse
就能轻松获得!经过这个快速介绍如何在 Python 中创建 CLI 应用后,您现在就可以深入研究
argparse
模块及其所有炫酷特性了。使用 Python 的 argparse 创建 CLI
您可以使用
argparse
模块为您的应用程序和项目编写用户友好的命令行界面。该模块允许您定义应用程序所需的参数和选项。然后,argparse
将负责为您解析sys.argv
的参数和选项。argparse
的另一个酷炫特性是它可以自动为您的 CLI 应用程序生成使用说明和帮助消息。该模块还会在参数无效时发出错误提示,并具备更多功能。在深入研究
argparse
之前,您需要知道该模块的文档可识别两种不同类型的命令行参数:在
ls.py
的示例中,path
是一个位置参数(positional argument)。这样的参数之所以被称为位置参数,是因为它在命令构造中的相对位置定义了其作用。与位置参数不同,可选参数(Optional arguments)并不是必需的。它们允许你修改命令的行为。以 Unix 命令
ls
为例,-l
标志就是一个可选参数,它使得命令以详细模式显示输出。在明确了这些概念之后,你就可以着手使用 Python 和
argparse
库来构建自己的命令行界面(CLI)应用程序了。创建命令行参数解析器
命令行参数解析器是任何使用
argparse
的命令行界面(CLI)中最为关键的部分。你在命令行上提供的所有参数和选项都会经过这个解析器的处理,它会为你完成繁重的解析工作。要使用
argparse
创建命令行参数解析器,您需要实例化ArgumentParser
类:ArgumentParser
的构造函数接受多种不同的参数,你可以利用这些参数来微调你的 CLI 的多个特性。由于所有这些参数都是可选的,因此你可以通过不传入任何参数直接实例化ArgumentParser
来创建一个最基本的解析器。在本教程中,你将会更深入地了解
ArgumentParser
构造函数的参数,特别是在定制你的参数解析器的部分。目前,你可以开始使用argparse
创建 CLI 的下一步了。这一步就是通过解析器对象来添加参数和选项。添加参数和选项
要为
argparse
的 CLI 添加参数和选项,你将使用ArgumentParser
实例的.add_argument()
方法。请注意,这个方法对参数和选项都是通用的。在argparse
的术语中,参数被称为位置参数(positional arguments),而选项被称为可选参数(optional arguments)。.add_argument()
方法的第一个参数决定了参数和选项之间的区别。这个参数被标识为名称(name)或标志(flag)。因此,如果你提供一个 name,那么你将定义一个参数(argument)。相反,如果你使用一个 flag,那么你将添加一个选项(option)。你已经在使用
argparse
时处理过命令行参数了。现在,考虑以下你自定义的ls
命令的增强版本,它向 CLI 添加了一个-l
选项:在这个例子中,第 11 行代码创建了一个带有
-l
和--long
标志的选项。参数(arguments)和选项(options)在语法上的区别在于,选项名称以短横线-
开头表示简写标志,以双短横线--
开头表示完整标志。请注意,在这个特定例子中,与
-l
或--long
选项一同设置了一个action
参数为"store_true"
,这意味着这个选项将存储一个布尔值。如果你在命令行上提供了这个选项,那么它的值将为True
。如果你省略了这个选项,那么它的值将为False
。在 "设置 Option 背后的 Action" 部分内容中,你将了解更多关于.add_argument()
中的action
参数的信息。在第 21 行的
build_output()
函数中,当long
为True
时,它会返回一个详细的输出,否则返回一个简短的输出。详细的输出将包含目标目录中所有条目的大小、修改日期和名称。它使用了诸如Path.stat()
这样的工具,以及带有自定义字符串格式的datetime.datetime
对象。继续在
sample
上执行您的程序,以检查-l
选项如何工作:新增的
-l
选项允许你生成并显示关于目标目录内容的更详细输出。既然你已经知道了如何向 CLI 添加命令行参数和选项,接下来就是深入解析这些参数和选项的时候了。这将是你在接下来部分要探索的内容。
解析命令行参数和选项
解析命令行参数是基于
argparse
的任何 CLI 应用中的另一个重要步骤。一旦你解析了参数,你就可以根据它们的值来执行相应的操作。在你自定义的ls
命令示例中,参数解析发生在包含args = parser.parse_args()
语句的行上。这个语句调用了
.parse_args()
方法,并将其返回值赋给args
变量。.parse_args()
的返回值是一个Namespace
对象,其中包含了在命令行上提供的所有参数和选项以及它们对应的值。考虑以下简单的示例:
通过在命令行参数解析器上调用
.parse_args()
方法得到的Namespace
对象,你可以使用点表示法(dot notation)访问所有输入参数、选项以及它们对应的值。这样,你就可以检查输入参数和选项的列表,并根据用户在命令行上的选择来执行相应的操作。你将在应用程序的主代码中使用这个
Namespace
对象。这与你在自定义ls
命令示例中的for
循环下所做的类似。到目前为止,你已经了解了创建基于
argparse
的 CLI 的主要步骤。现在,你可以花些时间学习如何在 Python 中组织和构建 CLI 应用程序的基础知识了。设置 CLI 应用程序的布局和构建系统
在继续你的
argparse
学习之旅之前,你应该暂停一下,思考如何组织你的代码和规划一个 CLI 项目。首先,你应该考虑以下几点:__main__.py
模块。将这些想法铭记于心,并考虑到模型-视图-控制器(MVC)模式是一种有效组织应用程序结构的方法,你在规划 CLI 项目时可以采用以下目录结构:
hello_cli/
目录是项目的根目录。在那里,您将放置以下文件:pip
的-r
选项,来自动安装这些依赖项。接下来是
hello_cli/
目录,它包含了应用的核心包,该包包含以下模块:__init__.py
文件使得hello_cli/
可以作为一个 Python 包被识别。__main__.py
文件提供了应用程序的入口点脚本(entry-point script)或可执行文件,这是启动程序的主要入口。cli.py
文件为应用提供了命令行界面。在此文件中的代码将扮演基于 MVC 架构中的视图-控制器角色。model.py
文件包含了支持应用主要功能的代码。这部分代码将在你的 MVC 布局中扮演模型角色。你还需要一个
tests/
包,其中包含针对应用程序组件的单元测试文件。在这个具体的项目布局示例中,你有test_cli.py
用于检查 CLI 功能的单元测试,以及test_model.py
用于检查你的模型代码的单元测试。pyproject.toml
文件允许你定义应用程序的构建系统以及许多其他常规配置。以下是一个如何为你的示例 hello_cli 项目填写此文件的简单示例:[build-system]
表头将setuptools
设置为应用程序的构建系统,并指定 Python 需要安装哪些依赖项来构建应用程序。[project]
表头为你的应用提供了通用元数据。这些元数据在你想要将应用发布到 Python 包索引(PyPI)时非常有用。最后,[project.scripts]
表头定义了你的应用程序的入口点。经过这次对 CLI 项目布局和构建的快速探索,你已经准备好继续学习
argparse
了,特别是如何自定义你的命令行参数解析器。自定义你的命令行参数解析器
在前面的部分中,你已经学习了如何使用 Python 的
argparse
模块为你的程序或应用实现命令行接口的基础知识。同时,你也了解了如何按照 MVC 模式组织和规划 CLI 应用项目。在接下来的部分中,你将更深入地探索
argparse
的许多其他强大功能。特别是,你将学习如何在ArgumentParser
构造函数中使用一些最有用的参数,这将使你能够自定义 CLI 应用的一般行为。调整程序的 Help 和 Usage 内容
向 CLI 应用程序的用户提供使用说明和帮助是一种最佳实践,可以通过出色的用户体验 (UX) 让用户更加愉快。在本节中,你将了解如何利用
ArgumentParser
的一些参数来微调 CLI 应用程序向用户显示帮助和使用消息的方式。你将学习如何:首先,你将开始设置你的程序名称,并指定该名称在帮助或使用说明消息中的显示方式。
设置程序名称
默认情况下,
argparse
会使用sys.argv
中的第一个值来设置程序的名称。这个第一项包含你刚刚执行的 Python 文件的名称。这个文件名在使用说明消息中看起来会有些奇怪。例如,继续使用
-h
选项运行自定义ls
命令:命令输出中的高亮行显示
argparse
正在使用文件名ls.py
作为程序的名称。这看起来有些奇怪,因为在使用说明消息中,应用名称很少包含文件扩展名。幸运的是,你可以使用
prog
参数来指定你的程序名称,就像下面的代码片段所示:使用
prog
参数,你可以指定将在使用说明消息中使用的程序名称。在这个例子中,你使用了字符串 "ls"。现在,继续运行你的应用:很好!这个输出的第一行中的使用说明消息显示程序名称为
ls
,而不是ls.py
。除了设置程序名称外,
argparse
还允许你定义应用的描述和结尾信息。在接下来的部分中,你将学习如何进行这两方面的操作。定义程序的描述和结语消息
你还可以为你的应用定义一个通用的描述和一个结尾或结束语。为此,你将分别使用
description
和epilog
参数。接下来,请更新ls.py
文件,在ArgumentParser
构造函数中添加以下内容:在这次更新中,
description
参数允许你为应用提供一个通用的描述。这个描述将显示在帮助消息的开头。epilog
参数则允许你定义一些文本作为应用的结尾或结束语。请注意,你可以使用旧式的字符串格式化操作符(%
)将prog
参数插入到epilog
字符串中。如果再次运行该应用程序,你将得到如下输出:
现在,输出会在使用消息之后显示描述消息,并在帮助文本末尾显示结语消息。
显示参数和选项的分组帮助
帮助分组(Help groups)是
argparse
的另一个有趣特性。它们允许你将相关的命令和参数进行分组,从而帮助你组织应用的帮助消息。要创建这些帮助分组,你将使用ArgumentParser
的.add_argument_group()
方法。作为一个例子,请考虑你自定义的
ls
命令的以下更新版本:在这次更新中,你为显示一般输出的参数和选项创建了一个帮助分组,并为显示详细输出的参数和选项创建了另一个分组。
如果你在命令行中使用
-h
选项运行应用程序,那么你将获得以下输出:现在,你的应用的参数和选项在帮助消息中以描述性的标题进行了方便的分组。这个整洁的特性将帮助你为用户提供更多的上下文,并帮助他们更好地理解应用的工作原理。
为参数和选项提供全局设置
除了自定义使用说明和帮助消息外,
ArgumentParser
还允许你对命令行界面(CLI)应用进行其他一些有趣的调整。这些调整包括:有时,你可能需要为你的应用的参数和选项指定一个全局默认值。你可以通过在调用
ArgumentParser
构造函数时,将默认值传递给argument_default
参数来实现这一点(注意:实际上ArgumentParser
没有argument_default
这个参数,但这里是为了说明可以全局设置默认值的概念。在实际应用中,你可能需要为每个参数单独设置默认值)。这个特性可能并不常用,因为参数和选项通常具有不同的数据类型或意义,很难找到一个满足所有需求的值。
然而,
argument_default
(尽管ArgumentParser
并没有直接提供这个参数,但这里是为了说明概念)的一个常见用例是当你想要避免将参数和选项添加到Namespace
对象中。在这种情况下,你可以使用SUPPRESS
常量作为默认值。这个默认值将确保只有命令行中提供的参数和选项才会被存储在arguments
的Namespace
中。作为一个例子,请继续修改你的自定义
ls
命令,如下面的代码片段所示:通过将
SUPPRESS
传递给ArgumentParser
构造函数,你可以防止未提供的参数被存储在argparse.Namespace
对象中。这就是为什么在调用build_output()
之前,你需要检查-l
或--long
选项是否实际被传递了。否则,你的代码会因为args
中不存在long
属性而引发AttributeError
错误。ArgumentParser
的另一个酷炫功能是允许你从外部文件中加载参数值。当你有一个具有冗长或复杂的命令行结构的应用,并希望自动化加载参数值的过程时,这个功能就非常有用。在这种情况下,你可以将参数值存储在一个外部文件中,并让你的程序从该文件中加载它们。为了尝试这个功能,请继续创建一个简单的命令行界面(CLI)应用,如下所示:
在这里,你向
ArgumentParser
的fromfile_prefix_chars
参数传递@
符号。然后,你创建了三个必须在命令行中提供的必需参数。现在,假设你经常使用此应用程序,并且总是使用相同的一组参数值。为了简化和优化你的工作流程,你可以创建一个文件,其中包含所有必需参数的适当值,每个参数占一行,就像下面的
args.txt
文件一样:有了这个文件,您现在可以调用您的程序并指示它从
args.txt
文件加载值,如以下命令运行所示:在此命令的输出中,你可以看到
argparse
已经读取了args.txt
的内容,并顺序地将值分配给了fromfile.py
程序的每个参数。所有参数及其值都已成功存储在Namespace
对象中。接受缩写选项名称的能力是
argparse
命令行界面(CLI)的另一个酷炫特性。这个特性是默认启用的,当你的程序具有长选项名称时非常有用。例如,考虑以下程序,它会在命令行下打印出你在--argument-with-a-long-name
选项后指定的值:这个程序会打印出你在
--argument-with-a-long-name
选项后传入的任何参数值。现在,请继续运行以下命令来检查 Python 的argparse
模块如何处理这些缩写:这些示例展示了如何缩写
--argument-with-a-long-name
选项的名称,而应用程序仍能正常工作。此功能是默认启用的。如果你希望禁用它并禁止缩写,那么可以在ArgumentParser
中使用allow_abbrev
参数:将
allow_abbrev
设置为False
会禁用命令行选项中的缩写。从这一点开始,你将需要为程序提供完整的选项名称才能正确工作。否则,你会收到一个错误:第二个示例中的错误消息告诉你
--argument
选项没有被识别为有效的选项。要使用该选项,你需要提供它的完整名称。微调你的命令行参数和选项
到目前为止,你已经学习了如何定制
ArgumentParser
类的多个功能,以改善你的命令行界面(CLI)的用户体验。现在,你知道了如何调整你的应用程序的使用说明和帮助信息,以及如何微调命令行参数和选项的一些全局方面。在本节中,你将学习如何定制你的 CLI 的命令行参数和选项的其他几个功能。在这种情况下,你将使用
.add_argument()
方法及其一些最相关的参数,包括action
、type
、nargs
、default
、help
等。设置 Option 背后的 Action
当你向命令行界面添加一个选项或标志时,通常需要定义如何将选项的值存储在调用
.parse_args()
后得到的Namespace
对象中。为此,你会使用.add_argument()
的action
参数。action
参数的默认值为 "store",意味着提供的选项值将原样存储在Namespace
中。action
参数可以接受几个可能的值。以下是这些可能值的列表及其含义:Beta Was this translation helpful? Give feedback.
All reactions