本文参考这篇文章,比较了argparse、click的区别。尽管只用某一个也不是不可以,但我觉得我还是挺有必要进行一下对应的对比。
本文的目标: 本文计划实现一个带有子命令的命令行工具,同时带有全局级别的配置参数(比如数据文件地址等)。在这个前提下使用不同的标准来比较所提到的三个命令行库。 (原本计划把docopt也顺便学习一下的,但实在是没什么必要,这次就算了吧。)
下文将按照参考文章的目录进行组0织,我觉得它这个组织格式还挺有道理的
分别用三种来实现子命令下共享嵌套的情况(自己动手),均失败。 目标场景:SO上的同款问题。对于给定的子命令read
和write
,父命令有一个--format
参数,如何使得python main.py read --format=xxx
成立。
click的写法依赖于decorator
@click.command()
声明子命令@click.option('--xxx',default=x,help=xxx)
与@click.argument('argumentName')
声明参数@click.group()
用来实现嵌套命令# click.group示例,执行python main.py initdb,python main.py dropdb即可,但是--debug只能在子命令下执行
import click
@click.group()
@click.option('--debug',default=False)
def cli(debug):
click.echo(f'Debug mode is {debug}')
@click.command()
def initdb():
click.echo('Initialized the database')
@click.command()
def dropdb():
click.echo('Dropped the database')
cli.add_command(initdb)
cli.add_command(dropdb)
if __name__ == '__main__':
cli()
在argparse中,子命令的实现是通过add_subparsers
来实现的
import argparse
def do_command_one(arg):
print('command1', arg)
print(arg.cmd1_option1)
print(arg.foo)
def do_command_two(arg):
print('command1', arg)
print(arg.cmd1_option1)
print(arg.cmd1_option2)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--foo', type=str)
subparsers = parser.add_subparsers(help='Functinos')
parser_1 = subparsers.add_parser('model', help='This is function about model')
parser_1.add_argument('--cmd1_option1', type=str)
parser_1.set_defaults(func=do_command_one)
parser_2 = subparsers.add_parser('model2', help='This is function about model')
parser_2.add_argument('--cmd2_option1', type=str)
parser_2.set_defaults(func=do_command_two)
args = parser.parse_args()
if args.func:
args.func(args)
上面这个即为我第一次学习argparse写的实例。这个脚本文件声明了两个子命令:model
和model2
,model
会有一个命令行参数--cmd1_option1
,model2
会有一个命令行参数--cmd2_option1
,同时全局会有一个参数--foo
。
此外,使用set_defaults
来设置了子命令的处理函数,以应对可能需要进行单独处理的情况。
唯一的问题是,全局参数的实现比价违背一般的习惯。python main.py --foo="test" model --cmd1_option1="test2"
才能通过,如果把--foo
放在子命令之后是无法识别的。
(pythonProject) wutianyang@TIANYANGWU-MB0 ~/PycharmProjects/commandTest python main.py model --cmd1_option1="test" --foo="123"
usage: main.py [-h] [--foo FOO] {model,model2} ...
main.py: error: unrecognized arguments: --foo=123
除此之外,这种写法只允许运行子命令的程序才能够通过编译,这个应该是写法的问题。
需求本身是很简单的,最关键的一点就是子命令之间要共享部分全局参数
SO上这个回答还挺不错的。执行python main.py create -p="db"
,从效果上来说确实是与预期一致。
import argparse
# Same main parser as usual
parser = argparse.ArgumentParser()
# Usual arguments which are applicable for the whole script / top-level args
parser.add_argument('--verbose', help='Common top-level parameter',
action='store_true', required=False)
# Same subparsers as usual
subparsers = parser.add_subparsers(help='Desired action to perform', dest='action')
# Usual subparsers not using common options
parser_other = subparsers.add_parser("extra-action", help='Do something without db')
# Create parent subparser. Note `add_help=False` and creation via `argparse.`
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('-p', help='add db parameter', required=True)
# Subparsers based on parent
parser_create = subparsers.add_parser("create", parents=[parent_parser],
help='Create something')
# Add some arguments exclusively for parser_create
parser_update = subparsers.add_parser("update", parents=[parent_parser],
help='Update something')
# Add some arguments exclusively for parser_update
if __name__ == '__main__':
arg = parser.parse_args()
print(arg)
print(arg.action) # action
print(arg.p) # db
click中也有类似的写法,不过是基于decorator改造的,也挺有意思的。SO问题。主要是我对于decorator也没什么研究,之后有时间了再进一步学习。