Python 命令行之旅:深入 argparse(二)
作者:HelloGitHub-Prodesire
HelloGitHub 的《讲解开源项目》系列,项目地址:https://github.com/HelloGitHub-Team/Article
前言
在上一篇“深入 argparse(一)”的文章中,我们深入了解了 argparse
的包括参数动作和参数类别在内的基本功能,具备了编写一个简单命令行程序的能力。本文将继续深入了解 argparse
的进阶玩法,一窥探其全貌,助力我们拥有实现复杂命令行程序的能力。
本系列文章默认使用 Python 3 作为解释器进行讲解。
若你仍在使用 Python 2,请注意两者之间语法和库的使用差异哦~
帮助
自动生成帮助
当你在命令行程序中指定 -h
或 --help
参数时,都会输出帮助信息。而 argparse
可通过指定 add_help
入参为 True
或不指定,以达到自动输出帮助信息的目的。
>>> import argparse
>>> parser = argparse.ArgumentParser(add_help=True)
>>> parser.add_argument('--foo')
>>> parser.parse_args(['-h'])
usage: [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO
如果 add_help=False
,那么在命令行中指定 -h
则会报错:
>>> import argparse
>>> parser = argparse.ArgumentParser(add_help=False)
>>> parser.add_argument('--foo')
>>> parser.parse_args(['-h'])
usage: [--foo FOO]
: error: unrecognized arguments: -h
自定义帮助
ArgumentParser
使用 formatter_class
入参来控制所输出的帮助格式。
比如,通过指定 formatter_class=argparse.RawTextHelpFormatter
,我们可以让帮助内容遵循原始格式:
>>> import argparse
>>> parser = argparse.ArgumentParser(
... add_help=True,
... formatter_class=argparse.RawTextHelpFormatter,
... description="""
... description
... raw
... formatted"""
... )
>>> parser.add_argument(
... '-a', action="store_true",
... help="""argument
... raw
... formatted
... """
... )
>>>
>>> parser.parse_args(['-h'])
usage: [-h] [-a]
description
raw
formatted
optional arguments:
-h, --help show this help message and exit
-a argument
raw
formatted
对比下不指定 formatter_class
的帮助输出,就可以发现 descirption 和 -a 两个帮助内容上的差异:
>>> import argparse
>>> parser = argparse.ArgumentParser(
... add_help=True,
... description="""
... description
... notraw
... formatted"""
... )
>>> parser.add_argument(
... '-a', action="store_true",
... help="""argument
... notraw
... formatted
... """
... )
>>> parser.parse_args(['-h'])
usage: [-h] [-a]
description notraw formatted
optional arguments:
-h, --help show this help message and exit
-a argument notraw formatted
参数组
有时候,我们需要给参数分组,以使得在显示帮助信息时能够显示到一起。
比如某命令行支持三个参数选项 --user
、--password
和--push
,前两者需要放在一个名为 authentication
的分组中以表示它们是身份认证信息。那么我们可以用 ArgumentParser.add_argument_group
来满足:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> group = parser.add_argument_group('authentication')
>>> group.add_argument('--user', action="store")
>>> group.add_argument('--password', action="store")
>>> parser.add_argument('--push', action='store')
>>> parser.parse_args(['-h'])
usage: [-h] [--user USER] [--password PASSWORD] [--push PUSH]
optional arguments:
-h, --help show this help message and exit
--push PUSH
authentication:
--user USER
--password PASSWORD
可以看到,当我们输出帮助信息时,--user
和 --password
选项都出现在 authentication
分组中。
选项参数前缀
不知你是否注意到,在不同平台上命令行程序的选项参数前缀可能是不同的。比如在 Unix 上,其前缀是 -
;而在 Windows 上,大多数命令行程序(比如 findstr
)的选项参数前缀是 /
。
在 argparse
中,选项参数前缀默认采用 Unix 命令行约定,也就是 -
。但它也支持自定义前缀,下面是一个例子:
>>> import argparse
>>>
>>> parser = argparse.ArgumentParser(
... description='Option prefix',
... prefix_chars='-+/',
... )
>>>
>>> parser.add_argument('-power', action="store_false",
... default=None,
... help='Set power off',
... )
>>> parser.add_argument('+power', action="store_true",
... default=None,
... help='Set power on',
... )
>>> parser.add_argument('/win',
... action="store_true",
... default=False)
>>> parser.parse_args(['-power'])
Namespace(power=False, win=False)
>>> parser.parse_args(['+power', '/win'])
Namespace(power=True, win=True)
在这个例子中,我们指定了三个选项参数前缀 -
、+
和/
,从而:
- 通过指定选项参数
-power
,使得power=False
- 通过指定选项参数
+power
,使得power=True
- 通过指定选项参数
/win
,使得win=True
共享解析器
有些时候我们需要共享解析器,以共享里面的参数配置。比如,我们的命令行工具需要支持对阿里云和 AWS 进行操作,两类操作都需要指定 AccessKeyId
和 AccessKeySecret
来表明用户身份和权限。那么共享解析器就显得尤为必要,这样就可以少去重复代码。
我们可以这样做,在 base.py
中定义一个父解析器,存放 AccessKey
相关参数配置,作为公用的解析器。由于后续的子解析器会自动生成帮助信息,这里的父解析器指定 add_help=False
以不自动生成帮助信息:
# bash.py
import argparse
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--ak-id', action="store")
parser.add_argument('--ak-secret', action="store")
然后就可以分别在 ali.py
和 aws.py
中分别定义子解析器,通过 parents
入参指定上述父解析器,从而继承公共的参数,并实现各自的参数:
# ali.py
import argparse
import base
parser = argparse.ArgumentParser(
parents=[base.parser],
)
parser.add_argument('--ros',
action="store_true",
default=False,
help='Using ROS service to orchestrate cloud resources')
print(parser.parse_args())
# aws.py
import argparse
import base
parser = argparse.ArgumentParser(
parents=[base.parser],
)
parser.add_argument('--cloudformation',
action="store_true",
default=False,
help='Using CloudFormation service to orchestrate cloud resources')
print(parser.parse_args())
最终通过 -h
参数分别看 ali.py
和 aws.py
所支持的参数,其中共同参数为 --ak-id
和 --ak-secret
,特定参数分别为 --ros
和 --cloudformation
:
$ python3 ali.py -h
usage: ali.py [-h] [--ak-id AK_ID] [--ak-secret AK_SECRET] [--ros]
optional arguments:
-h, --help show this help message and exit
--ak-id AK_ID
--ak-secret AK_SECRET
--ros Using ROS service to orchestrate cloud resources
$ python3 aws.py -h
usage: aws.py [-h] [--ak-id AK_ID] [--ak-secret AK_SECRET] [--cloudformation]
optional arguments:
-h, --help show this help message and exit
--ak-id AK_ID
--ak-secret AK_SECRET
--cloudformation Using CloudFormation service to orchestrate cloud
resources
嵌套解析器
我们之前介绍的命令行中,使用形式通常是 cli --a --b xxx
。但还有一种极为常见的命令行使用方式是 cli subcmd --a --b xxx
。比如当我们要通过 git
推送标签时,会用到 git push --tags
。
通过实现嵌套解析器,我们可以很容易地对这种子命令的形式进行解析。
在嵌套解析器中,我们定义一个父解析器来作为整个命令行的入口,再分别定义N个子解析器来对应N个子命令,由此即可实现整个功能。
在下面这个例子中,我们支持 create
和 delete
两个子命令,用来创建或删除指定路径。而 delete
命令支持 --recursive
参数来表明是否递归删除指定路径:
# cli.py
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='commands')
# Create
create_parser = subparsers.add_parser(
'create', help='Create a directory')
create_parser.add_argument(
'dirname', action='store',
help='New directory to create')
# Delete
delete_parser = subparsers.add_parser(
'delete', help='Remove a directory')
delete_parser.add_argument(
'dirname', action='store', help='The directory to remove')
delete_parser.add_argument(
'--recursive', '-r', default=False, action='store_true',
help='Recursively remove the directory',
)
print(parser.parse_args())
直接指定 -h
来查看所支持的子命令和参数选项:
$ python3 cli.py -h
usage: cli.py [-h] {create,delete} ...
positional arguments:
{create,delete} commands
create Create a directory
delete Remove a directory
optional arguments:
-h, --help show this help message and exit
直接指定 delete -h
来查看 delete
子命令支持的参数选项:
$ python3 cli.py delete -h
usage: cli.py delete [-h] [--recursive] dirname
positional arguments:
dirname The directory to remove
optional arguments:
-h, --help show this help message and exit
--recursive, -r Recursively remove the directory
自定义动作
在上一篇“深入 argparse (一)”的文章中介绍过8种参数动作,可以说是覆盖了绝大部分场景。但是也会有一些特定需求无法被满足,比如希望获取到的参数值都是大写。在这种情况下,自定义动作就派上了用场。
实现一个自定义动作类,需继承自 argparse.Action
,这个自定义动作类要传入到 ArgumentParser.add_argument
的 action
入参。当解析器解析参数时,会调用该类的 __call__
方法,该方法的签名为 __call__(self, parser, namespace, values, option_string=None)
,其中:
- parser 为解析器实例
- namespace 存放解析结果
- values 即命令行中传入的参数值
- option_string 为参数选项
在下面的例子中,我们通过 --words
传入单词,并在自定义动作类中将其值转换为大写:
# cli.py
import argparse
class WordsAction(argparse.Action):
def __call__(self, parser, namespace, values,
option_string=None):
print(f'parser = {parser}')
print(f'values = {values!r}')
print(f'option_string = {option_string!r}')
values = [v.upper() for v in values]
setattr(namespace, self.dest, values)
parser = argparse.ArgumentParser()
parser.add_argument('--words', nargs='*', action=WordsAction)
results = parser.parse_args()
print(results)
$ python3 cli.py --words foo bar
parser = ArgumentParser(prog='cli.py', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
values = ['foo', 'bar']
option_string = '--words'
Namespace(words=['FOO', 'BAR'])
小节
通过对 argparse
由浅入深的介绍,相信你已经全面了解了 argparse
的威力,也具备了开发命令行工具的能力。但“纸上得来终觉浅,绝知此事要躬行”。
在下篇文章中,将带大家一起用 argparse
实现日常工作中常见的 git
命令,想想是不是有些兴奋呢?
Python 命令行之旅:深入 argparse(二)的更多相关文章
- Python 命令行之旅 —— 初探 argparse
『讲解开源项目系列』启动--让对开源项目感兴趣的人不再畏惧.让开源项目的发起者不再孤单.跟着我们的文章,你会发现编程的乐趣.使用和发现参与开源项目如此简单.欢迎联系我们给我们投稿,让更多人爱上开源.贡 ...
- Python 命令行之旅 —— 深入 argparse (一)
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python 命令行之旅:使用 argparse 实现 git 命令
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python 命令行之旅:使用 docopt 实现 git 命令
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python 命令行之旅:深入 click 之参数篇
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python 命令行之旅:深入 click 之选项篇
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- Python 命令行之旅:使用 click 实现 git 命令
作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...
- python命令行参数解析模块argparse和docopt
http://blog.csdn.net/pipisorry/article/details/53046471 还有其他两个模块实现这一功能,getopt(等同于C语言中的getopt())和弃用的o ...
- Python命令行参数解析模块argparse
当写一个Python脚本时经常会遇到不同参数不同功能的情况,如何做一个更好看的命令帮助信息以及对命令参数解析呢? 这就需要使用argparse模块 #!/usr/bin/env python # -* ...
随机推荐
- idea的安装与配置及基本用法
Intellij IDEA 确实使用更加方便,由于目前只用到maven项目,所以此处只记录maven项目的配置. 一.配置idea前准备: 1.下载idea安装包.jdk安装包.maven安装包.gi ...
- 【ML入门】李宏毅机器学习笔记01-Learning Map
版权声明:小博主水平有限,希望大家多多指导.本文仅代表作者本人观点,转载请联系知乎原作者——BG大龍. 目录 1 什么是机器学习? 2 机器学习的3个步骤 3 李宏毅老师的机器学习课程 4 按“模型的 ...
- Java_Map接口
Map接口 1.1 Map接口概述 我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图. Collection中的集合,元素是孤立存在 ...
- Unity3D热更新之LuaFramework篇[07]--怎么让unity对象绑定Lua脚本
前言 在上一篇文章 Unity3D热更新之LuaFramework篇[06]--Lua中是怎么实现脚本生命周期的 中,我分析了由LuaBehaviour来实现lua脚本生命周期的方法. 但在实际使用中 ...
- 【iOS】判断苹果的设备是哪种
有时候需要判断苹果的设备是 iPhone 还是 iPad 等其他设备,示例代码如下: if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUs ...
- Java 字符串分隔 split
Java中的我们可以利用 split 方法(Java.lang.string.split)把字符串按照指定的分割符进行分割,然后返回字符串数组,下面是string.split的用法实例及注意事项. s ...
- Gridea+GitHub搭建个人博客
某日闲余时间看到一篇介绍Gridea博客平台的文章,大概看了一下觉得此平台还不错,随即自己进入Gridea官网瞅了瞅.哇,这搭建过程也太简单了吧,比Hexo博客搭建要容易很多,而且还有后台管理客户端, ...
- Superset 官方入门教程中文翻译
本文翻译自 Superset 的官方文档:Toturial - Creating your first dashboard 最新版本的 Superset 界面与功能上与文档中提到的会有些许出入,以实际 ...
- 算法与数据结构基础 - 哈希表(Hash Table)
Hash Table基础 哈希表(Hash Table)是常用的数据结构,其运用哈希函数(hash function)实现映射,内部使用开放定址.拉链法等方式解决哈希冲突,使得读写时间复杂度平均为O( ...
- (通俗易懂小白入门)网络流最大流——EK算法
网络流 网络流是模仿水流解决生活中类似问题的一种方法策略,来看这么一个问题,有一个自来水厂S,它要向目标T提供水量,从S出发有不确定数量和方向的水管,它可能直接到达T或者经过更多的节点的中转,目前确定 ...