作者:HelloGitHub-Prodesire前言在前面三篇介绍argparse的文章中,我们对argparse的能力有了全面的了解,相信很多小伙伴已经摩拳擦掌想要入手了创建您自己的命令行工具。本文将以我们日常工作中最常用的git命令为例,讲解如何使用argparse库实现一个真正好用的命令行程序。本系列文章默认使用Python3作为解释器。如果你还在使用Python2,请注意两者在语法和库使用上的差异~不妨回忆一下常用的git命令。最常用的git子命令是什么?当你写完一段代码或添加或删除一些文件后,你将使用以下命令来查看文件状态:gitstatus确认文件状态后,你将使用以下命令添加一个或多个文件(文件夹)到暂存区:gitadd[pathspec[pathspec...]]然后使用如下命令提交消息:gitcommit-m"yourcommitmessage"最后使用以下命令将commit推送到远程仓库:gitpush我们将使用argparse和gitpython库来实现这4个子命令。关于gitpythongitpython是用于与git存储库交互的Python的第三方库。我们将借用它的能力来实现真正的git逻辑。安装:pipinstallgitpython思考在实现之前,我们不妨想一下argparse会用到哪些功能?整个程序的结构是怎样的?argparse要实现子命令,前面介绍的嵌套解析器是必不可少的。当用户键入一个子命令时,该子命令对应的subparser需要响应,所以需要使用subparse的set_defaults函数,gitadd[pathspec[pathspec...]],我们需要实现位置参数,而数量是任意的。对于gitcommit--messagemsg或者gitcommit-mmsg,我们需要实现option参数,可以是长的也可以是短的Option程序结构命令行程序需要一个cli函数作为统一的入口,负责构建parser并解析命令行参数。我们还需要四个handle_xxx函数来响应相应的子命令。基本结构如下:importosimportargparsefromgit.cmdimportGitdefcli():"""gitnamedprogramentry"""passdefhandle_status(git,args):"""handlestatuscommand"""passdefhandle_add(git,args):"""handleaddcommand"""passdefhandle_commit(git,args):"""handle-mcommand"""passdefhandle_push(git,args):"""handlepushcommand"""passif__name__=='__main__':下面的cli()将一步步实现我们的git程序。该实现假设我们在argparse-git.py文件中实现我们的git程序。构建解析器我们需要构建一个父解析器作为程序的根解析器,程序名指定为git。然后在其上添加一个子解析器,为后续子命令的解析做准备:defcli():"""git命名程序入口"""parser=argparse.ArgumentParser(prog='git')add_subparsers(title='ThesearecommonGitcommandsusedinvarioussituations',metavar='command')add_subparsers中的title和metavar参数主要用于命令行帮助信息,最终效果如下:usage:git[-h]命令。..可选参数:-h,--help显示此帮助信息并退出这些是在各种情况下使用的常见Git命令:并将其对应的处理函数指定为handle_status。defcli():...#statusstatus_parser=subparsers.add_parser('status',help='显示工作树状态')status_parser.set_defaults(handle=handle_status)需要注意的是在status_parser.set_defaults函数中,它可以接受任意名称的关键字参数,这个参数的值会在父解析器解析命令行参数后保存在变量中。比如本文的示例程序中,我们为每个子解析器定义一个句柄,那么args=parser.parse_args()中的args就会有一个句柄属性,我们传入不同的子命令,那么这个句柄就是不同的反应函数。定义状态子解析器后,我们可以通过实现handle_status来实现状态命令的响应:defhandle_status(git,args):"""handlethestatuscommand"""cmd=['git','status']output=git.execute(cmd)print(output)不难看出,我们最终还是调用了真正的gitstatus来实现,打印输出。你可能会被handle_status的函数签名搞糊涂了,这里git和args是怎么传入的?这实际上是在我们自己的控制之下,将在本文末尾进行说明。添加子命令同样,我们需要在cli函数中添加一个用于解析add命令的子解析器add_parser,并将其对应的处理函数指定为handle_add。额外要做的事情是在子解析器add_parser上添加一个pathspec位置参数,它的数量是任意的:defcli():...#addadd_parser=subparsers.add_parser('add',help='Addfilecontentstotheindex')add_parser.add_argument('pathspec',help='Filestoaddcontentfrom',nargs='*')add_parser.set_defaults(handle=handle_add)然后,要实现handle_add函数,我们需要使用args。pathspec表示文件路径:defhandle_add(git,args):"""handleaddcommand"""cmd=['git','add']+args.pathspecoutput=git.execute(cmd)print(output)commitsubcommand同样,我们需要在cli函数中添加一个用于解析commit命令的subparsercommit_parser,并指定其对应的处理函数为handle_commit。额外要做的事情是向子解析器commit_parser添加一个-m/--message选项参数,这是必需的:defcli():...#commitcommit_parser=subparsers.add_parser('commit',help='记录对存储库的更改')commit_parser.add_argument('--message','-m',help='使用给定的作为提交消息',metavar='msg',required=True)commit_parser.set_defaults(handle=handle_commit)然后,要实现handle_commit功能,我们需要用到代表commit信息的args.message:defhandle_commit(git,args):"""handle-mcommand"""cmd=['git','commit','-m',args.message]output=git.execute(cmd)print(output)pushsubcommand同样,我们需要在cli函数push_parser中添加一个subparse用于解析push命令,并指定其对应的处理函数为handle_push。它的实现方式与状态子命令相同:defcli():...#pushpush_parser=subparsers.add_parser('push',help='Updateremoterefsalongwithassociatedobjects')push_parser.set_defaults(handle=handle_push)然后,实现handle_push函数,类似于handle_status:defhandle_push(git,args):cmd=['git','push']output=git.execute(cmd)print(output)解析参数定义后parent-childparser,添加参数后,我们需要解析参数。这个工作也在cli函数中实现:defcli():...git=Git(os.getcwd())args=parser.parse_args()ifhasattr(args,'handle'):args.handle(git,args)else:parser.print_help()通过git.cmd.Git实例化一个git对象,用于与git仓库交互,通过parser.parse_args()解析命令,行通过hasattr(args,'handle')确定是否输入了子命令。由于每个子解析器都定义了一个handle,如果用户没有在命令行输入任何命令,args没有handle属性,那么我们输出帮助信息。如果用户输入了子命令,则调用args.handle,通过Entergit和args对象来处理相应的命令至此,我们实现了一个简单的git命令行,使用pythonargparse-git.py-h查看帮助如下:usage:git[-h]command...optionalarguments:-h,--helpshowthishelpmessageandexit这些是在各种情况下使用的常用Git命令:commandstatus显示工作树状态add添加文件内容到theindexcommit记录对版本库的更改push连同关联对象一起更新remoterefs然后我们就可以愉快的使用我们自己搭建的git程序了!查看完整源码请戳argparse-git.py。总结本文简单介绍了日常工作中常用的git命令,然后提出了实现的思路,最后使用argparse和gitpython一步步实现了git程序。是不是很有成就感?关于argparse的讲解到此结束。回顾一下argparse的四步,加上今天的内容,感觉还是挺清晰简单的。不过,这只是打开了命令行之门。有没有想过,argparse的四个步骤虽然简单易懂,但是有点麻烦。有更容易的方法吗?如果我对命令行帮助语法非常熟悉,我可以写一个帮助字符串来定义所有的命令行元信息吗?那么直接轻松获取解析出的参数信息就可以了?在下一篇文章中,我将向大家讲解另一种站在全新的思维方式上,非常强大的librarydocopt。欢迎关注HelloGitHub公众号获取更多开源项目的资讯和内容。《解释开源项目系列》上线——让对开源项目感兴趣的人不再害怕,让开源项目的发起者不再孤单。关注我们的文章,您将发现编程的乐趣,使用并发现参与开源项目是多么容易。欢迎联系我们投稿,让更多的人爱上开源,为开源做贡献~