Python之参数传入

方法一:sys.argv

这个模块是我在工作中最常见的其他人写的Python脚本传入命令行参数的方式,也是最简单(粗暴)的方式:

  • sys.argv 是命令行参数列表
  • len(sys.argv) 是命令行参数个数
  • sys.argv[0] 表示脚本名

实例

import sys
def test_for_sys(year, name, body):
    print('the year is', year)
    print('the name is', name)
    print('the body is', body)
if __name__ == '__main__':
    try:
        year, name, body = sys.argv[1:4]
        test_for_sys(year, name, body)
    except Exception as e:
        print(sys.argv)
        print(e)

一般我的 Python 脚本的启动函数就像上的例子一样,可能需要传入几个必要参数,这个时候,如果我们在使用命令行传入的时候,就可以使用 sys.argv 这个属性,任何一个 Python 脚本在启动的时候都有这个属性,它是一个列表,列表的第一个参数是脚本的命令,列表后面的参数就是命令行传入的参数,所以可以在脚本中提取这些参数传入到函数中运行。
执行以上代码,输出结果为:

G:\Allcodes\testscripts>python test_cmd.py 2018 Leijun "are you ok?"
the year is 2018
the name is Leijun
the body is are you ok?

很好理解,由于 sys.argv 是一个由参数组成的列表,所以如果脚本中需要的参数比你命令行中输入的多,那肯定会报错,因为你输入的参数不够,反过来,如果你输入的参数比函数需要的多,那么无所谓,多的参数因为不会被提取使用,所以不影响脚本运行。

小结

sys.argv 形式传入参数的方式比较简单,但是也很死板,因为传入的参数是一个有序的列表,所以在命令行中必须按照脚本规定的顺序去输入参数,这种方法比较适合脚本中需要的参数个数很少且参数固定的脚本

方法二:getopt模块

getopt模块是专门处理命令行参数的模块,用于获取命令行选项和参数,也就是sys.argv。命令行选项使得程序的参数更加灵活。支持短选项模式(-)和长选项模式(--)。
该模块提供了两个方法及一个异常处理来解析命令行参数。

方法

getopt.getopt

getopt.getopt 方法用于解析命令行参数列表,语法格式如下:
getopt.getopt(args, options[, long_options])

参数说明

  • args: 要解析的命令行参数列表。
  • options: 以字符串的格式定义,options后的冒号(:)表示该选项必须有附加的参数,不带冒号表示该选项不附加参数。
  • long_options: 以列表的格式定义,long_options 后的等号(=)表示如果设置该选项,必须有附加的参数,否则就不附加参数。
    该方法返回值由两个元素组成: 第一个是 (option, value) 元组的列表。 第二个是参数列表,包含那些没有'-'或'--'的参数。

getopt.gnu_getopt

异常处理

Exception getopt.GetoptError

在没有找到参数列表,或选项的需要的参数为空时会触发该异常,异常的参数是一个字符串,表示错误的原因,属性 msg 和 opt 为相关选项的错误信息。

实例

import sys, getopt
def main(argv):
   inputfile = ''
   outputfile = ''
   try:
      opts, args = getopt.getopt(argv,"hi:o:",["ifile=","ofile="])
   except getopt.GetoptError:
      print ('test.py -i <inputfile> -o <outputfile>')
      sys.exit(2)
   for opt, arg in opts:
      if opt == '-h':
         print ('test.py -i <inputfile> -o <outputfile>')
         sys.exit()
      elif opt in ("-i", "--ifile"):
         inputfile = arg
      elif opt in ("-o", "--ofile"):
         outputfile = arg
   print ('输入的文件为:', inputfile)
   print ('输出的文件为:', outputfile)

if __name__ == "__main__":
   main(sys.argv[1:])

执行以上代码,输出结果为:

$ python3 test.py -h
usage: test.py -i <inputfile> -o <outputfile>

$ python3 test.py -i inputfile -o outputfile
输入的文件为: inputfile
输出的文件为: outputfile

方法三:argparse 模块

argparse 模块也是 Python 自带的一个命令行参数模块,这个模块才是真的为了命令行参数而生的模块,相较之下 sys.argv 只是碰巧可以用在命令行参数上面而已。

参数

在添加命令行参数的属性的时候,还可以有更多的设置,如下:

  • name or flags:也就是在使用参数的时候使用的符号,--foo 或者 -f
  • action:根据我的理解,这个属性可以选择参数在只提供符号而不输入实际的值的时候给予一个默认的值
  • nargs:这个属性规定了参数可以输入的个数
  • const:这属性跟 action 属性一起使用
  • default:这属性就是给参数设置一个默认值
  • type:这个属性规定了参数的数据类型
  • choices:这个属性给参数限定了一个选择的迭代,参数只能在这个范围内选择参数的值,否则报错
  • required:参数的值为必填

实例

import argparse

def test_for_sys(year, name, body):
    print('the year is', year)
    print('the name is', name)
    print('the body is', body)

parser = argparse.ArgumentParser(description='Test for argparse')
parser.add_argument('--name', '-n', help='name 属性,非必要参数')
parser.add_argument('--year', '-y', help='year 属性,非必要参数,但是有默认值', default=2017)
parser.add_argument('--body', '-b', help='body 属性,必要参数', required=True)
args = parser.parse_args()

if __name__ == '__main__':
    try:
        test_for_sys(args.year, args.name, args.body)
    except Exception as e:
        print(e)

使用 argparse 模块的方式也挺简单的,首先需要构建一个参数实例,也就是代码中的parser = argparse.ArgumentParser(description='Test for argparse')
这行代码就生成了一个命令行参数的对象,之后就可以给对象添加一些参数属性,最后只需要从属性从提取传入的参数进行使用即可。
上面的代码添加了3个参数,添加参数的前两个字段很容易理解,--name-n 都可以用来在命令行中使用,都表示了参数 name,这样后面使用 parse_args() 方法获取到所有参数之后,就可以使用 args.name 这种形式来提取对应的参数。
首先来看看使用了命令行参数之后脚本的“帮助”:

G:\Allcodes\testscripts>python test_cmd.py --help
usage: test_cmd.py [-h] [--name NAME] [--year YEAR] --body BODY

Test for argparse

optional arguments:
  -h, --help            show this help message and exit
  --name NAME, -n NAME  name 属性,非必要参数
  --year YEAR, -y YEAR  year 属性,非必要参数,但是有默认值
  --body BODY, -b BODY  body 属性,必要参数

可以看到,脚本生成了一个 help,这样就可以在脚本中对每个参数的使用进行一些描述,方便其他人更加了解每个参数的含义,方便使用。
看一下运行成功的几个命令,首先是不传入 year 参数,而使用默认的参数:

G:\Allcodes\testscripts>python test_cmd.py -n Leijun --body "are you ok?"
the year is 2017
the name is Leijun
the body is are you ok?

然后是不提供 name 参数,但是不会报错,最后可以看到从参数中会得到一个 None 并传入到了脚本中进行使用:

G:\Allcodes\testscripts>python test_cmd.py --body "are you ok?"
the year is 2017
the name is None
the body is are you ok?

最后看一个报错的执行结果:

G:\Allcodes\testscripts>python test_cmd.py
usage: test_cmd.py [-h] [--name NAME] [--year YEAR] --body BODY
test_cmd.py: error: the following arguments are required: --body/-b

可以看到上面的报错提示了 body 参数是一个必要参数,不能少。

小结

其实我非常喜欢这个内置的命令行参数模块,因为它不仅方便使用,更重要的是它就是内置的,不需要单独安装依赖。

方法四:click 库

ClickFlask 的团队 pallets 开发的优秀开源项目,它为命令行工具的开发封装了大量方法,使开发者只需要专注于功能实现。这是一个第三方库,专门为了命令行而生的非常有名的 Python 命令行模块。

实例

import click

@click.command()
@click.option('--name',default='Leijun',help='name 参数,非必须,有默认值')
@click.option('--year',help='year 参数',type=int)
@click.option('--body',help='body 参数')
def test_for_sys(year, name, body):
    print('the year is', year)
    print('the name is', name)
    print('the body is', body)

if __name__ == '__main__':
    test_for_sys()

运行结果:

G:\Allcodes\testscripts>python test_cmd.py --year 2019
the year is 2019
the name is Leijun
the body is None

可以看到 click 是使用装饰器的方式给函数添加命令行属性,比较特殊的是最后调用函数的时候是没有带上参数的,因为参数会自动通过命令行的形式传入。其他设置参数的属性跟前面的 argparse 的方式非常相似,具体的参数可以参考文档和其他的教程用法,这里就不做过多的说明。

小结

click 库也是一个非常人性化的命令行参数模块,它其实非常强大,强大到把所有的命令行参数可能涉及的情况都考虑到了,需要自己去探索。

总结

以上就是我接触和使用到的四种给Python脚本设置命令行参数的方法,其中第一种是我在工作中见到的其他同事写的脚本中的方式,但是我并不喜欢这种方式,因为它真的太死板了;我最喜欢的是 argparse模块,这个模块是内置模块,但是功能完全够用,非常方便。