Python 解析命令行一般使用 ArgumentParser
来进行解析,先创建 ArgumentParser
的实例,然后调用该实例的 add_argument
函数来添加待解析的参数模型。最后调用该实例的 parse_args
函数来解析参数,解析后再调用相应的命令。使用这种方法不仅麻烦,还容易出错。本文利用 Python 的函数式编程来对 argparse 模块进行封装,以达到简化程序编写的目的。
主要程序代码
# 模块:命令行处理模块
# 作者:黄涛
# License:GPL
# Email:[email protected]
# 创建:2017-01-18 21:34
# 修改:2017-02-05 为__call__函数增加位置参数,可以支持主程序为classmethod
# 修改:2017-02-11 调整相关函数名
from functools import partial
__all__='command','arg'
class _Command(object):
kwargs={} # ArgumentParser 的参数,由command函数生成
allow_empty=False # 是否允许参数为空,如允许则argv为None的情况下也调用执行函数
def __init__(self,run):
self.run=run # 可执行函数,命令行解析成功时调用
self.args=[] # 参数列表
self.subcommands=[] # 子命令列表
@classmethod
def wrapper(cls,cmd,*args,**kw):
def _(fn):
if not isinstance(fn,cls):
fn=cls(fn)
getattr(fn,cmd)(*args,**kw)
return fn
return _
def add_subcommand(self,subcommand):
self.subcommands.append(subcommand)
def add_arg(self,*args,**kw):
self.args.append((args,kw))
def command(self,*subcommands,allow_empty=False,**kwargs):
self.kwargs=kwargs # ArgumentParser 的参数
self.subcommands=list(subcommands) # 子命令
self.allow_empty=allow_empty # 是否允许命令行为空
def proc_args(self,parser):
for args,kw in reversed(self.args):
parser.add_argument(*args,**kw)
def __call__(self,*args,argv=None):
import sys
from argparse import ArgumentParser
argv=argv or sys.argv[1:]
parser=ArgumentParser(**self.kwargs)
self.proc_args(parser)
parser.set_defaults(proc=self.run)
if self.subcommands:
subparsers=parser.add_subparsers(help='Sub command')
for subcommand in self.subcommands:
help=subcommand._kw.pop('description')
if help:
subcommand._kw['help']=help
sub=subparsers.add_parser(subcommand.run.__name__,
**subcommand.kwargs)
subcommand.proc_args(sub)
sub.set_defaults(proc=subcommand.run)
if self.allow_empty or argv:
kwargs=dict(parser.parse_args(argv)._get_kwargs())
proc=kwargs.pop('proc',None)
if proc:
try:
proc(*args,**kwargs)
except Exception as e:
print(str(e))
else:
parser.print_usage()
command=partial(_Command.wrapper,'command')
arg=partial(_Command.wrapper,'add_arg')
使用方法
该模块主要对外提供 command
和 arg
两个函数。这两个函数都是装饰器函数。都可以把一个普通的函数变成一个命令行。但作用略有差异,其主要功能如下:
arg
函数的用法
arg
函数是一个装饰器函数,用于将一个普通函数封装成命令行解析参数,同时向其添加一个参数。其参数与 ArgumentParser.add_argument
完全一致。一个函数可以添加一个或多个参数模型。示例代码如下:
from click import arg,command
@arg('-f','--foo',action='store_true',help='foo')
@arg('-b','--bar',metavar='bar',help='bar')
def main(foo=False,bar=None):
print(foo,bar)
if __name__=='__main__':
main()
command
函数的用法
command
函数也是用于将一个函数封装成命令行,但它的作用是传递命令行的相关参数。一个普通函数只能用 command
调用一次,也可以不调用。其位置参数是子命令列表,关键字参数将作为 ArgumentParser
的构造函数的参数传入。示例代码如下:
from click import arg,command
@command(prog='test',description='示例程序')
@arg('-f','--foo',action='store_true',help='foo')
@arg('-b','--bar',metavar='bar',help='bar')
def main(foo=False,bar=None):
print(foo,bar)
if __name__=='__main__':
main()
其他说明
arg
函数和 command
函数均可以根据需要使用,只要保证同一个函数只调用一次 command
函数即可。
通上面的封装,我们可以只使用 arg
和 command
两个函数即可实现 argparse
模块的大部分功能,是不是简化很多?