python如何优雅地在命令行传入参数?sys,argparse,click使用

前言

一些大型的python项目经常性会提供命令行的接口使得同样一个文件能够根据参数的不同执行不同的操作。常见的如python main.py --name='zhangsan' --age=20,这让用户能够需求选择不同的服务启动并选择不同的参数配置,实现这一操作的方法有很多,比较常见的是三种:sys,argparse,click

sys

syspython自带的模块,可以通过sys.argv获取到输入的参数,如下所示:

import sys

def hello():
    print(sys.argv)

if __name__ == '__main__':
    hello()

#  python3 test.py hey 23
# >> ['test.py', 'hey', '23']

sys.argv返回一个列表,第一项是执行的文件名,后面依次是输入的未指定参数。

该模块十分简单易懂且方便使用,不过缺点也很明显,无法指定参数的名称,输入的参数必须按照指定顺序,且没有help打印帮助信息(指明每个参数的作用),需要开发者手动创建一个。

argparse

argparse是一个非常好用的命令行参数工具,相比于sys,它能够指定参数名称,参数默认值,参数的描述信息等,具体如下:

import argparse

def hello(args):
    print(args)
    print(args.name,args.age)

if __name__ == '__main__':
    my_arg = argparse.ArgumentParser('My argument parser')
    my_arg.add_argument('--name','-n',default='cp',type=str,help='Your name')
    my_arg.add_argument('--age','-a',default=24,type=int,help='Your age')
    args = my_arg.parse_args()
    hello(args)


通过声明一个ArgumentParser对象,再为其添加相应的输入参数,最后进行parse_args()参数解析就可以获得从命令行输入的参数。这种方式的好处主要包括:

  • 参数顺序随意,按照参数名称指定且名称(--name)可以用简称(-a)减少输入
  • 包含help帮助信息,可以让使用者快速理解每个参数的含义
  • 可以设定默认值和数据类型

但这种方式必须要给出参数名称,不像sys那样按顺序输入参数即可。

click

click这个模块也是我近期才用到的,在一些大型项目中出现较多(特别是flask的项目),用click能够用更少的代码实现与argparse类似的功能,且看起来更加简洁。

import click

@click.command()
@click.option('--name',default= 'cp',type=str,help='Your name')
@click.option('--age',default=24, type=int,help='Your age')
def hello(name,age):
    print(name,age)


if __name__ == '__main__':
    hello()


可以发现click将一个函数(hello())封装成了一个命令行接口,函数的输入参数由option提供的参数决定。需要注意的是hello(name,age)中的name,age参数名是与option指定的命令行参数名称对应的,因此不能任意改成其他名称。click.option还有一个参数required可以设置参数是否需要,如果设置False,且没有给定默认值,那返回结果是None,如果设定True且没有输入值则会报错。

  • click.group
    click还有一个重要的方法group,他允许将多个函数接口连接起来,形成一组,然后依次执行,具体实例如下:

    import click
    
    @click.group()
    def one():
        print("hello one")
    
    @click.command(name='second')
    @click.option("--name", help="Your name")
    def two(name):
        print("hello two: {}".format(name))
    
    @click.command()
    @click.option("--age", help="Your age")
    def three(age):
        print("hello three:{}".format(age))
    
    if __name__ == '__main__':
        one.add_command(two)
        one.add_command(three)
        one()


    在这个例子中,我给two()函数的command添加了name属性,使得在调用的时候可以用second替代two进行接口调用。上面的方式的好处在于被group装饰的函数是我们确定一定要执行的,而另外两个加入group的函数则根据我们命令行参数的输入执行。

  • click.argument
    除了click.option添加参数以外,click还可以通过click.argument添加参数,与option不同的是,argument直接指定参数名(没有--),且是按照顺序输入(有点类似sys.argv),如下例子:

    import click
    
    @click.command()
    @click.argument('ip',type=str)
    @click.argument('port',type=int)
    def hello(ip, port):
        print("ip:{},port:{}".format(ip,port))
    
    if __name__ == '__main__':
        hello()
    
    # python3 test.py '10.1.1.1' 1234
    # >> ip:10.1.1.1,port:1234

总结

本文讲述了三种在命令行执行python文件时传入参数的方式,sys胜在简便,在一般个人的小项目中可以图方便使用,argparse更为正式,对参数的传递有严格的把控,click兼具上面两者,且代码可读性强,适合大型的项目。三者根据自己需要选择使用。