通过命令行运行脚本,设置合理的命令或者参数,可以让脚本使用更加友好,避免频繁修改源文件中的hard code导致的效率低下问题。

同时,这是也是践行python的理念,让程序变得更加Pythonic!更酷!

一 介绍

开发过程中我们可能需要让我们的程序接受一些参数,以便执行不同的功能,而不需要修改代码。

为了实现这个功能,我们可以自己解析main函数的参数列表,也可以使用一些命令行工具。

命令行工具有很多,比如argparse,fire,docopt,当然,包括click,不同工具有不同的“属性”,选择最合适自己习惯的一款即可。

命令行常用的参数类型:

参数,必须要设定的值,以便程序能够运行,比如find命令必须指定路径

选项,可选的参数,可以是各种类型,开关,数值,区间等等,比如ls命令可以指定路径,否则默认是当前路径

子命令,给主命令添加子命令,同样可以拥有各种参数,类似git config,config就是git支持的子命令,config也有自己的参数

二 安装

使用pip3安装click:

pip3 install --user click

--user表示安装到用户目录,更多时候都建议使用这个选项,可以避免一些权限问题。

三 使用

使用Click的一般步骤为:

使用@click.command()装饰指定函数,使之成为命令行接口;

使用@click.argument()或者@click.option()装饰函数,为其添加命令行选项等。

1 最简单的示例

下面是click的最简单的形式,后续以此为框架实现不同功能。

使用@click.command()修饰函数main,就可以让它成为一条命令,目前它只能接受参数--help。

import click
@click.command()
def main():
print('Hello click')
if __name__ == "__main__":
main()

通过命令行执行可以正常输出,如果指定--help则会打印出它的帮助内容。如下:

# python3 click_demo.py
Hello click
# python3 click_demo.py --help
Usage: click_demo.py [OPTIONS]
Options:
--help Show this message and exit.

2 添加必备参数

在click中,通过@click.argument('param-name')指定必备参数。

click会将参数名解析为"param_name",即去掉前缀,横线改下划线传递给main函数,如果参数命名对不上,则会报错。

如下(代码基于前面的修改,以下只展示修改部分):

@click.command()
@click.argument('user-name')
def main(user_name):
print(f'Hello {user_name}')

此时执行help得到的提示是:

# python3 click_demo.py --help
Usage: click_demo.py [OPTIONS] USER_NAME
Options:
--help Show this message and exit.

其中全大写加下划线表示是必备参数,如果执行时不指定则会报错:

# python3 click_demo.py
Usage: click_demo.py [OPTIONS] USER_NAME
Try 'click_demo.py --help' for help.
Error: Missing argument 'USER_NAME'.

指定参数时则正常运行:

# python3 click_demo.py farmer
Hello farmer

click.argument()还可以指定参数类型、获取环境变量、选择项参数等功能,详见arguments。

3 添加可选参数

在click中,通过@click.option( )指定可选参数,以下是几种常用的方法:

指定参数缩写、完整名称、自定义解析后的变量名、设置默认值和参数解释:

@click.option('-p', '--param-name', 'custom_param_name', default='magic', help='Parameter introduce.')

只指定完整参数名(对应变量名将按约定方式命名),指定参数类型(默认为str),未指定参数时提示用户输入:

@click.option('--count', type=int, prompt='Input greet times', help='The times to greet.')

设定一个开关标识位,不接受参数值(这是一个trick,有更好的实现方式):

@click.option('-d', '--debug', is_flag=True, help='Enable debug mode')

将main函数修改如下:

@click.command()
@click.argument('user-name')
@click.option('-p', '--param-name', 'custom_param_name', default='magic', help='Parameter introduce.')
@click.option('--count', type=int, prompt='Input greet times', help='The times to greet.')
@click.option('-d', '--debug', is_flag=True, help='Enable debug mode')
def main(user_name, custom_param_name, count, debug):
if debug:
print(f'Enter debug mode')
for i in range(count):
print(f'Greet {i}: Hello {user_name}')
print(f'Custom parameter is: {custom_param_name}')

此时help输出的结果是:

# python3 click_demo.py --help
Usage: click_demo.py [OPTIONS] USER_NAME
Options:
-p, --param-name TEXT Parameter introduce.
--count INTEGER The times to greet.
-d, --debug Enable debug mode
--help Show this message and exit.

此时参数已经变得丰富了,此时:

使用时必须指定参数USER_NAME;

可以通过-p或者--param-name指定该参数的值,默认类型为字符串;

可以通过--count=3或者--count 3指定问候语打印次数为3次,类型为整型;如果没有指定,则会提示用户输入,并自动转换为整型;如果输入的字符串不能转换为整数,则会提示再次输入;

如果指定-d或者--debug则开启调试模式;默认为false,程序中获取到的值为None。

以下是一些演示:

# python3 click_demo.py farmer --count 3 -p 123
Greet 0: Hello farmer
Greet 1: Hello farmer
Greet 2: Hello farmer
Custom parameter is: 123
# python3 click_demo.py farmer -d
Input greet times: 3r
Error: 3r is not a valid integer
Input greet times: 3
Enter debug mode
Greet 0: Hello farmer
Greet 1: Hello farmer
Greet 2: Hello farmer
Custom parameter is: magic

@click.option( )还有更多用法,详见options。

4 添加子命令

前面我们都是给main函数添加了很多参数,我们可以将@click.command()修改为@click.group(invoke_without_command=True)。

添加参数invoke_without_command=True是指主命令可以不指定子命令单独执行。

此时我们要给main函数添加子命令,修饰符应该改为main.command(),以表示修饰的函数作为main函数的子命令,子命令同样可以拥有自己的参数。

示例如下:

@click.group(invoke_without_command=True)
@click.pass_context
def main(ctx):
if ctx.invoked_subcommand is None:
click.echo('I was invoked without subcommand')
else:
click.echo('I am about to invoke %s' % ctx.invoked_subcommand)
@main.command()
@click.argument('count')
def boo(count):
print('boo ' * count)

此时查看主命令和子命令的help:

# python3 click_demo.py --help
Usage: click_demo.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
boo
# python3 click_demo.py boo --help
I am about to invoke boo
Usage: click_demo.py boo [OPTIONS] COUNT
Options:
--help Show this message and exit.

可以看到主命令的帮助内容介绍了其子命令Commands,包括boo;boo子命令的--help会显示其参数介绍。

子命令会是在main函数执行完以后执行,正常执行亦是如此:

# python3 click_demo.py
I was invoked without subcommand
# python3 click_demo.py boo 5
I am about to invoke boo
boo boo boo boo boo

详细的@click.group()使用方法详见commands。