• [Python]标准库argparse解析命令行参数使用介绍


    前言

    argparse 是python标准库中用于解析命令行参数的模块。

    基本使用

    import argparse
    
    # 创建解析器
    ## description: 程序说明
    ## prog: 程序名, 默认为脚本名
    ## epilog: 帮助信息末尾附加文本
    ## formatter_class: 控制帮助信息的展示样式
    parser = argparse.ArgumentParser(
        prog="/my_tool",
        description="A simple command-line tool.",
        epilog="Example usage: /my_tool 'Hello World' --verbose"
    )
    
    # 使用add_argument() 添加参数
    # echo1和echo2为位置参数,使用时必需按顺序提供
    # type 指定类型转换函数
    # help 设置帮助说明
    parser.add_argument("echo1", type=str, help="echo something")
    parser.add_argument("echo2", type=str, help="echo something")
    
    # -x 或 --xx 为可选参数
    # default 设置默认值
    parser.add_argument("--sftp_ip",type=str,default="127.0.0.1", help="sftp服务的IP地址")
    
    # 自动转换类型成int
    # metavar 修改帮助信息中显示的占位名
    parser.add_argument("--sftp_port", metavar="PORT", type=int, default="22")
    
    # dest 指定解析后属性名, 解析后使用 args.username 访问
    parser.add_argument("--user-name", dest="username")
    
    # required 让可选参数变成必须提供
    # required 只适用于可选参数
    parser.add_argument("--name", required=True)
    
    # choice 限制可选值范围
    parser.add_argument("-H","--host",type=str, choices=["127.0.0.1", "192.168.0.10"])
    
    # nargs 控制参数个数
    ## nargs=2, 必须 2 个参数
    ## nargs='*', 0个或多个参数
    ## nargs='+', 1个或多个参数
    ## nargs='?', 0个或1个参数
    parser.add_argument('--nums', nargs=3, type=int)
    
    
    # 创建互斥组,-v和-q不能同时使用
    group = parser.add_mutually_exclusive_group()
    # action 定义参数行为
    group.add_argument("-v", "--verbose", action="store_true")
    group.add_argument("-q", "--quiet", action="store_true")
    
    # 解析参数
    # 返回的是一个 Namespace 对象
    args = parser.parse_args()
    
    # 使用命令参数
    print(f"echo1: {args.echo1}, echo2: {args.echo2}")
    print(f"sftp服务的IP为: {args.sftp_ip}, 端口号: {args.sftp_port}")
    print(f"host is {args.host}")
    

    argparse会自动生成帮助选项-h--help

    action参数

    add_argument() 中的 action 参数用来定义参数行为。默认值为store.

    • action='store_true', 常用于布尔开关, 不传则为 False
    parser.add_argument('--verbose', action='store_true')
    
    # 运行 python app.py --verbose
    # 解析后
    args.verbose == True
    
    • action='store_false', 反向布尔开关
    • action='append', 每出现一次就追加到列表
    parser.add_argument('--tag', action='append')
    
    # 使用
    python app.py --tag a --tag b
    
    # 得到
    args.tag == ['a', 'b']
    
    • action='count', 统计出现次数
    parser.add_argument('-v', '--verbose', action='count', default=0)
    
    # 使用
    python app.py -vvv
    
    # 得到
    args.verbose == 3
    
    • action='version', 打印版本
    parser.add_argument('--version', action='version', version='v1.0.0', default=0)
    
    # 使用
    python app.py --version
    # 输出 v1.0.0
    
    # 也可以写个函数来动态获取
    def get_version() -> str:
        return "v1.0.0"
    
    # help 不传的话默认为 show program's version number and exit
    parser.add_argument(
        "--version",
        action="version",
        version=get_version(),
        # help="Show the version of the tool and exit.",
    )
    
    • action='store_const', 设置为指定值
    parser.add_argument('--json', action='store_const', const='json', dest='format')
    
    # 运行
    python app.py --json
    
    # 得到
    args.format == 'json'
    

    互斥参数

    创建互斥组:group = parser.add_mutually_exclusive_group()

    使用group.add_argument设置的命令行选项将互斥,不能同时使用

    简单例子:

    import argparse
    
    parser = argparse.ArgumentParser()
    
    # 创建互斥组
    # 传入 required=True 的话,用户必须从互斥组参数中选一个; 默认用户可以不选可选组参数
    group = parser.add_mutually_exclusive_group()
    
    group.add_argument('--verbose', action='store_true')
    group.add_argument('--quiet', action='store_true')
    
    args = parser.parse_args()
    print(args)
    
    # 这样同时调用会报错
    python app.py --verbose --quiet
    

    子命令

    当命令行工具包含多种操作时可能就会需要子命令了,例如:

    python tool.py add user1
    python tool.py delete user1
    python tool.py list
    

    这里add, delete, list 都是子命令, 这时候最适合用 parser.add_subparsers()

    add_subparsers() 用来给一个命令行程序添加多个子解析器(subparsers),每个子解析器对应一个子命令。

    基本用法

    import argparse
    
    parser = argparse.ArgumentParser(prog='usercli', description='用户管理工具')
    # required=True, 强制用户使用子命令
    # 建议总是显式写 dest='command'
    subparsers = parser.add_subparsers(dest='command', required=True)
    
    # add 子命令
    parser_add = subparsers.add_parser('add', help='添加用户')
    parser_add.add_argument('username', help='用户名')
    parser_add.add_argument('--age', type=int, default=18, help='年龄')
    
    # delete 子命令
    parser_delete = subparsers.add_parser('delete', help='删除用户')
    parser_delete.add_argument('username', help='用户名')
    
    # list 子命令
    parser_list = subparsers.add_parser('list', help='列出用户')
    parser_list.add_argument('--verbose', action='store_true', help='显示详细信息')
    
    args = parser.parse_args()
    
    if args.command == 'add':
        print(f'添加用户: {args.username}, 年龄: {args.age}')
    elif args.command == 'delete':
        print(f'删除用户: {args.username}')
    elif args.command == 'list':
        print(f'列出用户, verbose={args.verbose}')
    

    子命令绑定处理函数

    上面基本用法中用if/elif 来分发逻辑,简单场景下还凑合,但是当命令多,功能复杂时,代码会显得非常混乱,这种情况下就可以为子命令绑定处理函数。

    示例代码:

    import argparse
    
    def handle_add(args):
        print(f'添加用户: {args.username}, 年龄: {args.age}')
    
    def handle_delete(args):
        print(f'删除用户: {args.username}')
    
    def handle_list(args):
        print(f'列出用户, verbose={args.verbose}')
    
    def create_parser() -> argparse.ArgumentParser:
        parser = argparse.ArgumentParser(prog='usercli')
        subparsers = parser.add_subparsers(dest='command', required=True)
    
        parser_add = subparsers.add_parser('add', help='添加用户')
        parser_add.add_argument('username')
        parser_add.add_argument('--age', type=int, default=18)
        parser_add.set_defaults(func=handle_add)
    
        parser_delete = subparsers.add_parser('delete', help='删除用户')
        parser_delete.add_argument('username')
        parser_delete.set_defaults(func=handle_delete)
    
        parser_list = subparsers.add_parser('list', help='列出用户')
        parser_list.add_argument('--verbose', action='store_true')
        parser_list.set_defaults(func=handle_list)
        return parser
    
    def main() -> None:
        parser = create_parser()
        args = parser.parse_args()
        # 如果用了 args.func(args)
        # 一定要确保每个子命令都执行了 parser_xxx.set_defaults(func=...)
        # 否则报错: AttributeError: 'Namespace' object has no attribute 'func'
        args.func(args)
    
    
    if __name__ == "__main__":
        main()
    

    子命令共享公共参数

    如果多个子命令都需要同一组参数,可以抽一个“父解析器”。

    import argparse
    
    def main():
        common_parser = argparse.ArgumentParser(add_help=False)
        common_parser.add_argument('--config', help='配置文件路径')
    
        parser = argparse.ArgumentParser(prog='tool')
        subparsers = parser.add_subparsers(dest='command', required=True)
    
        parser_a = subparsers.add_parser('start', parents=[common_parser])
        parser_a.add_argument('--port', type=int)
    
        parser_b = subparsers.add_parser('stop', parents=[common_parser])
        parser_b.add_argument('--force', action='store_true')
    
        # 解析命令行参数
        # common_parser 是一个共享参数模板, 不需要参与参数解析
        args = parser.parse_args()
        if args.command == 'start':
            print(f"Starting with config: {args.config} on port: {args.port}")
        elif args.command == 'stop':
            print(f"Stopping with config: {args.config} {'forcefully' if args.force else ''}")
        else:
            print("Unknown command")
    
    if __name__ == "__main__":
        main()
    

    子命令设置别名

    parser_remove = subparsers.add_parser('remove', aliases=['rm'])
    

    这样两种命令都可以

    python tool.py remove file.txt
    python tool.py rm file.txt
    

    嵌套子命令

    复杂 CLI 可能有多级命令,比如:

    tool user add alice
    tool user delete bob
    

    可以嵌套 add_subparsers()

    import argparse
    
    parser = argparse.ArgumentParser(prog='tool')
    subparsers = parser.add_subparsers(dest='entity', required=True)
    
    user_parser = subparsers.add_parser('user')
    user_subparsers = user_parser.add_subparsers(dest='action', required=True)
    
    user_add = user_subparsers.add_parser('add')
    user_add.add_argument('name')
    
    user_delete = user_subparsers.add_parser('delete')
    user_delete.add_argument('name')
    
    args = parser.parse_args()
    print(args)
    

    运行:

    python tool.py user add alice
    

    得到:

    Namespace(entity='user', action='add', name='alice')
    
  • 相关阅读:
    2.6 KNN(K近邻算法)
    count(1)、count(*) 与 count(列) 的区别?
    LeetCode75——Day12
    [CISCN 2019初赛]Love Math
    CVPR‘23投稿量再创新高? CCF会议投稿量大比拼, 谁才是卷王?
    爱奇艺、优酷、腾讯视频 限制设备登录 唯一登录设计原理
    【数据库事务日志碎片原理分析与方案】-分析篇
    lazada按关键字搜索商品 API 返回值说明
    【EMC专题】传导骚扰测试设备和方法
    论文的章节有重复率的要求吗?
  • 原文地址:https://www.cnblogs.com/XY-Heruo/p/20239962