序
所谓子命令,就是类似于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的输出也提示了其子命令为install、download、uninstall等。
子命令
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'
,分别代表赋值为True和False
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
时会报错:
说的是最后一行代码,因为没有输入参数,所以没
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
网友评论