写在前面
写了也蛮长时间的功能性脚本了,一直在苦恼有没有一个合适的三方库能解决脚本使用帮助的问题,之前对命令行进行创建的时候用的是getopt
库,相应的getopt
库是没有设置帮助的写法,所以还是用最为笨拙的办法,用print()
方法和函数进行输出。怎么说呢,虽然能达到预期效果但是编写过程中还是比较麻烦的,格式、维护问题等等。其他的办法也有用optparse
库的同好,这是Python2中的写法,我个人是以Python3为主的,并且总体用下来也不是很好用。
近期学习过程中看到了click
库,感觉这个库就比较好,简单学习一下,以后可以省点心了。
简介
Click
库是用 Python 写的一个第三方模块,用于快速创建命令行。除此之外,Python内置了一个Argparse
的标准库用于创建命令行,但使用起来有些繁琐,Click
相比于Argparse
,就好比requests
相比于urllib
。
基础用法
基础的使用方法分为两步:
- 使用
@click.command()
装饰一个函数,实质成为命令行的接口,获取命令行输入内容 -
使用
@click.option()
等装饰函数,为其添加命令行选项基础的用法结构如下:
import click
@click.command()
@click.option('-n','--name', default=default_value, help='name')
def main(param):
pass
一个简单的使用示例:
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name', help='The person to greet.')
def main(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
main()
上面的例子中函数main()
有两个参数,count
和name
,参数对应的值因为@click
的装饰从而在命令行中获取。
- @click.command() 使函数 hello 成为命令行接口;
- @click.option 的第一个参数指定了命令行选项的名称,参数
default
意为设置默认值;
同时从这里可以看出使用click.echo()
进行输出可以获得更好的兼容性,众所周知print()
函数在Python2和Python3中的语法存在差异。
对于@click.command()
可以使用context_settings
参数来设置获取帮助的参数名称。
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.command(context_settings=CONTEXT_SETTINGS)
def main():
pass
一些用法
click.option
option
最基本的用法就是通过指定命令行选项的名称,从命令行读取参数值,再将其传递给函数。除了设置命令行选项的名称,option
可以通过参数default
进行默认值的设置,通过参数help
设置命令行参数使用说明等。
- default: 设置命令行参数的默认值
- help: 参数使用说明
- type: 参数类型,可以是string,int,float等
- prompt: 当在命令行中没有输入相应的参数时,会根据prompt提示用户输入
- nargs: 指定命令行参数接收的值的个数
- metavar:如何在帮助页面表示值
- required:要求命令行参数为必填项
参数type
使用type
来指定参数类型
@click.command()
@click.option('--rate', type=float, help='rate') # 指定rate是float类型
def show(rate):
click.echo('rate: %s' % rate)
if __name__ == '__main__':
show()
使用type
指定参数可选值
import click
@click.command()
@click.option('--gender', type=click.Choice(['man', 'woman'])) # 限定值
def choose(gender):
click.echo('gender: %s' % gender)
if __name__ == '__main__':
choose()
# 执行情况:
# $ python click_choice.py --gender boy
# Usage: click_choice.py [OPTIONS]
#
# Error: Invalid value for "--gender": invalid choice: boy. (choose from man, woman)
#
# $ python click_choice.py --gender man
# gender: man
多值参数
如果一个参数需要接收多个值。option
支持设置固定长度的参数值,通过nargs
指定
import click
@click.command()
@click.option('--center', nargs=2, type=float, help='center of the circle')
@click.option('--radius', type=float, help='radius of the circle')
def circle(center, radius):
click.echo('center: %s, radius: %s' % (center, radius))
if __name__ == '__main__':
circle()
option
指定了两个参数:center
和radius
,其中,center
表示二维平面上一个圆的圆心坐标,接收两个值,以元组的形式将值传递给函数,而radius
表示圆的半径。
关于输入密码
在输入密码的时候,有时希望能隐藏显示。option
提供了两个参数来设置密码的输入:hide_input
和confirmation_promt
,其中,hide_input
用于隐藏输入,confirmation_promt
用于重复输入。
import click
@click.command()
@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True)
def input_password(password):
click.echo('password: %s' % password)
if __name__ == '__main__':
input_password()
对于例子中的写法有些复杂,click
库提供了一种更为简单的写法,使用@click.password_option()
以达到目的
import click
@click.command()
@click.password_option()
def input_password(password):
click.echo('password: %s' % password)
if __name__ == '__main__':
input_password()
参数prompt
参数prompt
其实来自于click.prompt()
函数,在需要用户输入时,就可以使用prompt()
函数。而在上述的例子中,在命令行中没有输入设置了prompt
的参数时,会根据prompt
提示用户输入。prompt=True
表示接受用户输入。默认情况下,它接受任何Unicode字符串,同时也可以要求任何其他类型。
value = click.prompt('Please enter a valid integer', type=int)
如果提供默认值,则会自动确定类型。
value = click.prompt('Please enter a number', default=42.0)
在此基础上延伸出来另外一个专职负责输入"确认"的函数click.confirm()
,询问用户是否想要继续某一个动作,默认情况下,它以布尔值的形式返回提示的结果。
if click.confirm('Do you want to continue?'):
click.echo('Well done!')
如果程序没有返回True
,也可以自动中止程序的执行
click.confirm('Do you want to continue?', abort=True)
改变命令行程序的执行
有些参数会改变命令行程序的执行,比如在终端输入python
是进入Python控制台,而输入python --version
是打印Python版本。Click 提供eager
标识对参数名进行标识,如果输入该参数,则会拦截既定的命令行执行流程,跳转去执行一个回调函数。
import click
def print_version(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.echo('Version 1.0')
ctx.exit()
@click.command()
@click.option('--version', is_flag=True, callback=print_version,
expose_value=False, is_eager=True)
@click.option('--name', default='Ethan', help='name')
def hello(name):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
# 执行情况
# $ python click_eager.py
# Hello Ethan!
#
# $ python click_eager.py --version # 拦截既定的命令行执行流程
# Version 1.0
#
# $ python click_eager.py --name Michael
# Hello Michael!
#
# $ python click_eager.py --version --name Ethan # 忽略 name 选项
# Version 1.0
- is_eager=True:表明该命令行选项优先级高于其他选项;
- expose_value=False:表示如果没有输入该命令行选项,会执行既定的命令行流程;
- callback:指定了输入该命令行选项时,要跳转执行的函数;
- is_flag=True:表明该参数的值只有True和False。
click.argument
@click.argument
可以用来添加固定参数。它的使用和option
类似,但支持的功能比option
少。
import click
@click.command()
@click.argument('coordinates')
def show(coordinates):
click.echo('coordinates: %s' % coordinates)
if __name__ == '__main__':
show()
# 运行情况
# $ python click_argument.py # 错误,缺少参数 coordinates
# Usage: click_argument.py [OPTIONS] COORDINATES
#
# Error: Missing argument "coordinates".
#
# $ python click_argument.py --help # argument 指定的参数在 help 中没有显示
# Usage: click_argument.py [OPTIONS] COORDINATES
#
# Options:
# --help Show this message and exit.
#
# $ python click_argument.py --coordinates 10 # 错误用法,这是 option 参数的用法
# Error: no such option: --coordinates
#
# $ python click_argument.py 10 # 正确,直接输入值即可
# coordinates: 10
多个argument:
import click
@click.command()
@click.argument('x')
@click.argument('y')
@click.argument('z')
def show(x, y, z):
click.echo('x: %s, y: %s, z:%s' % (x, y, z))
if __name__ == '__main__':
show()
# 运行情况
# $ python click_argument.py 10 20 30
# x: 10, y: 20, z:30
#
# $ python click_argument.py 10
# Usage: click_argument.py [OPTIONS] X Y Z
#
# Error: Missing argument "y".
#
# $ python click_argument.py 10 20
# Usage: click_argument.py [OPTIONS] X Y Z
#
# Error: Missing argument "z".
#
# $ python click_argument.py 10 20 30 40
# Usage: click_argument.py [OPTIONS] X Y Z
#
# Error: Got unexpected extra argument (40)
不定参数:
import click
@click.command()
@click.argument('src', nargs=-1)
@click.argument('dst', nargs=1)
def move(src, dst):
click.echo('move %s to %s' % (src, dst))
if __name__ == '__main__':
move()
# 运行情况
# $ python click_argument.py file1 trash # src=('file1',) dst='trash'
# move (u'file1',) to trash
# $ python click_argument.py file1 file2 file3 trash # src=('file1', 'file2', 'file3') dst='trash'
# move (u'file1', u'file2', u'file3') to trash
其中,nargs=-1
表明参数src
接收不定量的参数值,参数值会以tuple
的形式传入函数。如果nargs
大于等于1,表示接收nargs
个参数值,
彩色输出
如果需要使用彩色输出则需要colorama
库,同时输出函数要是用click.secho()
而并非click.echo()
import click
@click.command()
@click.option('--name', help='The person to greet.')
def hello(name):
click.secho('Hello %s!' % name, fg='red', underline=True)
click.secho('Hello %s!' % name, fg='yellow', bg='black')
if __name__ == '__main__':
hello()
- fg 表示前景颜色(即字体颜色),可选值有:BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE 等;
- bg 表示背景颜色,可选值有:BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE 等;
- underline 表示下划线,可选的样式还有:dim=True,bold=True 等;
Comments | NOTHING