美文网首页
给Python脚本带上子命令(sub-commands)

给Python脚本带上子命令(sub-commands)

作者: 钢琴师2005 | 来源:发表于2020-02-06 11:16 被阅读0次

    所谓子命令,就是类似于python的包管理工具pip,不止可以接受参数,还可以拥有子命令,子命令可以单独接受特有参数,这为构建cli(commandline interface)程序提供了莫大的帮助,把命令分门别类可以提高程序的可读性和操作的便捷性。

    之前写cli程序,都是接受一级参数,所有参数都是-a、-b这样的然后在程序中进行判断和识别,包括冲突选项的排除都得自己做,这样导致入口程序代码过长不说,因为每次写的都不一样导致代码不规范不统一,质量参差不齐,而且本来自己写的质量就差😥

    使用效果

    主入口
    子命令connect

    参考pip

    先看看优秀的子命令程序是怎么样的,以pip为例。

    参数

    pip -V:查看版本

    ~$ pip3 -V
    pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)
    

    pip -h:查看帮助

    ~$ pip3 -h
    
    Usage:
      pip3 <command> [options]
    
    Commands:
      install                     Install packages.
      download                    Download packages.
      uninstall                   Uninstall packages.
      ......
      help                        Show help for commands.
    
    General Options:
      -h, --help                  Show help.
      --isolated                  Run pip in an isolated mode, ignoring environment variables and user configuration.
      -v, --verbose               Give more output. Option is additive, and can be used up to 3 times.
      -V, --version               Show version and exit.
      ......
    

    上面-h的输出也提示了其子命令为installdownloaduninstall等。

    子命令

    pip install:安装包,通过-h我们可以看到,总共有5中用法,其中包含了常用的:

    pip install <package name> [options]:通过包名安装

    pip install -r requirements.txt [options]:通过依赖文件安装

    ~$ pip3 install -h
    
    Usage:
      pip3 install [options] <requirement specifier> [package-index-options] ...
      pip3 install [options] -r <requirements file> [package-index-options] ...
      ......
    
    Description:
      ......
    
      pip also supports installing from "requirements files", which provide
      an easy way to specify a whole environment to be installed.
    
    Install Options:
      -r, --requirement <file>    Install from the given requirements file. This option can be used multiple times.
      ......
    General Options:
      ......
    

    可以单独接收与pip不同的参数和位置参数,相当于一个独立的命令,这就是子命令存在的意义。

    具体实现

    本文用到了python3内置库argparse,官方文档https://docs.python.org/3/library/argparse.html,具体用法可以到上面一点点摸索,因为本文不涉及到argparse的所有用法。

    基本入口

    首先引用类argparse.ArgumentParser(),只需要填写其description参数:

    # _*_coding:utf-8 _*_
    # @Time    : 2020/2/5 18:41
    # @Author  : Shek 
    # @FileName: OneWayPipe_CLI.py
    # @Software: PyCharm
    import argparse
    
    parser = argparse.ArgumentParser(description='你的程序描述')
    

    创建子命令管理器

    接下来与普通的命令行程序直接调用add_argument()方法不同,我们由于是要构建包含子命令的程序,故应该调用add_subparsers()方法并赋值给subparsers变量:

    subparsers = parser.add_subparsers()
    

    注意,只允许出现一次parser.add_subparsers(),因为这是其他子命令的集合,是子命令的BOSS

    添加子命令bind

    接下来BOSS可以具体分配子命令了,使用add_parser()方法并赋值即可,如这里的bind子命令

    # command 'bind'
    cmd_bind = subparsers.add_parser('bind', help='bind server')
    

    这时候cmd_bind就可以继续添加只对其有效的参数了,使用add_argument()即可,如我需要两个参数protocol和addr,要求不输入的时候使用默认值,然后使用固定的位置来填入参数:

    cmd_bind.add_argument('protocol', action='store', nargs='?', default='tcp', help='communicate protocol')
    cmd_bind.add_argument('addr', action='store', nargs='?', default='*:4000', help='<host>:<port>')
    

    nargs:如果要不输入时就为默认值的话是必填项,赋值固定为nargs='?'

    action'store'代表存储输入的值并赋值到参数中,如addr参数在参数集args中的引用方式为args.addr,除此以外还有'store_true''store_false',分别代表赋值为TrueFalse

    help:-h时显示的帮助文本

    default:默认值

    获取参数后需指定一个函数来处理,构建一个函数sub_cmd_bind(arguments)

    def sub_cmd_bind(arguments):
        print('you are attempting to bind {}://{}'.format(arguments.protocol, arguments.addr))
    

    并用set_defaults()方法指定到cmd_bind

    cmd_bind.set_defaults(func=sub_cmd_bind)
    

    到此对bind子命令的配置就完成了

    添加子命令connect

    同理得:

    子命令connect配置代码
    不同的是cmd_connect中多了一个--keep-alive参数,这个参数是布尔类型的,加了就是True,不加就是False,另外带减号”-“的参数在引用的时候都要被转义成下划线”_“,如

    --keep-alive:使用时为arguments.keep_alive

    无参数时跳转

    子命令都完事儿了,现在需要加一行代码让程序对输入的参数跳转到指定的函数去(前面只是设置了各子命令的默认函数,但是程序还没有允许你直接跳过去):

    args = parser.parse_args()  # 处理输入的参数
    args.func(args)  # 跳转到对应的函数
    

    这样就可以了,但是存在一个瑕疵就是不带参数直接运行python chat.py时会报错:

    触发AttributeError
    说的是最后一行代码,因为没有输入参数,所以没func这个特性(也就是没生成这个函数让你去用啦),中间加一句判断:
    if not hasattr(args, 'func'):
        # 无参数时跳转到-h
        args = parser.parse_args(['-h'])
    

    这样就完成了。

    完整实现

    # _*_coding:utf-8 _*_
    # @Time    : 2020/2/5 18:41
    # @Author  : Shek 
    # @FileName: OneWayPipe_CLI.py
    # @Software: PyCharm
    import argparse
    
    
    def sub_cmd_bind(arguments):
        print('you are attempting to bind {}://{}'.format(arguments.protocol, arguments.addr))
    
    
    def sub_cmd_connect(arguments):
        print('you are attempting to connect {}://{}'.format(arguments.protocol, arguments.addr))
        if arguments.keep_alive:
            print('automatically reconnect enabled')
    
    
    parser = argparse.ArgumentParser(
        description='A LAN-chat program written in Python3 http://github.com/YourGithub/RepositoryAddress')
    subparsers = parser.add_subparsers()
    
    # command 'bind'
    cmd_bind = subparsers.add_parser('bind', help='bind server')
    cmd_bind.add_argument('protocol', action='store', nargs='?', default='tcp', help='communicate protocol')
    cmd_bind.add_argument('addr', action='store', nargs='?', default='*:4000', help='<host>:<port>')
    cmd_bind.set_defaults(func=sub_cmd_bind)
    
    # command 'connect'
    cmd_connect = subparsers.add_parser('connect', help='connect to a server')
    cmd_connect.add_argument('protocol', action='store', nargs='?', default='tcp', help='communicate protocol')
    cmd_connect.add_argument('addr', action='store', nargs='?', default='127.0.0.1:4000', help='<host>:<port>')
    cmd_connect.add_argument('--keep-alive', action='store_true', help='automatically reconnect when corrupted')
    cmd_connect.set_defaults(func=sub_cmd_connect)
    
    args = parser.parse_args()  # 处理输入的参数
    if not hasattr(args, 'func'):
        # 无参数时跳转到-h,否则会提示 namespace object has not attribute 'func',故这里用hasattr()判断
        args = parser.parse_args(['-h'])
    args.func(args)  # 跳转到对应的函数
    
    

    Vultr 2020年优惠:注册就送100美金,领取你的第一台VPS

    相关文章

      网友评论

          本文标题:给Python脚本带上子命令(sub-commands)

          本文链接:https://www.haomeiwen.com/subject/vnpyxhtx.html