• python 全网最优雅命令行参数解析, 没有之一


    背景

    我们在编写python程序时,程序中经常会提供多种功能或者模式,在实际使用时根据不同的参数使用不同的功能。那么如何获取命令行传入进来的参数呢?

    一般方法

    一般情况下,我们会使用 sys 模块,如👇

    import sys
    
    # 打印 sys 模块获取到的命令行参数
    print(sys.argv)
    
    • 1
    • 2
    • 3
    • 4

    或者,我们会使用 getopt 模块,如👇

    import getopt
    
    opts,args=getopt.getopt(sys.argv[1:],"i:ho:",["help","input=","output="])
    # 打印选项列表
    print(opts)
    # 打印参数值列表
    print(args)
    
    # 解析参数对应的值
    for opts,arg in opts:
    	print(opts)
    	if opts=="optName":
    		print("optName is value is:", arg)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    再或者,我们使用 argparse 模块,如👇

    import argparse
    
    parser = argparse.ArgumentParser()
    
    parser.add_argument('-a', '--arg1', help='argument 1')
    parser.add_argument('-b', '--arg2', help='argument 2')
    parser.add_argument('-c', '--arg3', help='argument 3')
    
    args = parser.parse_args()
    
    print(args.arg1)
    print(args.arg2)
    print(args.arg3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    但,以👆这些,都不优雅,如果我们需要在不同的函数或者模块中传递使用命令行进来的参数,那这些零散的参数处理代码,将会带来不小的麻烦。我们需要通过一个专门的类来封装命令行的参数。

    引入 DebugInfo 模块

    pip install DebugInfo

    定义一个命令行参数类,

    定义一个入参类,继承自 入参基类,来专门负责解析和管理我们的传入参数,示例见👇:

    # -*- coding:UTF-8 -*-
    
    # region 引入必要依赖
    from DebugInfo.DebugInfo import *
    
    # endregion
    
    class 入参类(入参基类):
        def __init__(self):
            # 初始化基类
            super().__init__(接口说明='这个脚本是用来演示如何使用【入参基类】来管理传入参数的')
    
    
    if __name__ == '__main__':
        # 打印一下 入参基类 的说明文档
        print(入参基类.__doc__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    👆上面的代码初步建立了入参类,继承自入参基类,我们通过打印入参基类doc 信息,可以查阅到相关的使用说明,如👇
    DebugInfo 入参基类 __doc__信息
    如👆,我们通过 doc 的说明信息得知,这个入参基类其实是在 argparse 的基础上做了二次封装,然后提供了一些易用的接口。🤭

    👇下面的代码为入参类添加了我们需要接收的参数,并实例化了入参类,并解析了命令行参数:

    # -*- coding:UTF-8 -*-
    
    # region 引入必要依赖
    from DebugInfo.DebugInfo import *
    
    # endregion
    
    class 入参类(入参基类):
        def __init__(self):
            # 初始化基类
            super().__init__(接口说明='这个脚本是用来演示如何使用【入参基类】来管理传入参数的')
    
            # 添加需要接收的参数
            self._添加参数('a', int, '这是 a 参数, 请输入一个数字', 0)
            self._添加参数('b', int, '这是 b 参数, 请输入一个数字', 0)
            self._添加参数('运算', ['和', '差'], '请输入运算形式', '和')
    
    
    if __name__ == '__main__':
        # 实例化入参类
        入参 = 入参类()
    
        # 解析命令行参数
        入参.解析入参()
    
        # 打印获取到的参数
        print(入参.get('a'))
        print(入参.get('b'))
        print(入参.get('运算'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    👆上面的代码运行效果如👇:
    DebugInfo 入参基类 解析命令行参数效果
    当我们在命令行中使用 -h 参数时,还会有相应的参数帮助提示,如👇
    DebugInfo 入参基类 命令行参数提示
    当我们输入的参数不正确时,还有相应的错误提示,如👇
    DebugInfo 入参基类 命令行参数错误提示
    这么多功能,相对于我们输入的代码量来说,是不是物超所值?优雅至极?

    百尺杆头,更进一步

    上面的代码中,我们通过 入参.get(‘a’) 这样的方式获取了参数 a 的值.这不够优雅.
    如👇的代码中,我们通过在 入参类 中定义property的方法,将每一个参数成员定义对应的getter接口,这样就可以通过 入参.a 这种方式获取和使用参数值了.同时也获得了 IDE 的成员提示和代码补全支持.

    # -*- coding:UTF-8 -*-
    
    # region 引入必要依赖
    from DebugInfo.DebugInfo import *
    
    # endregion
    
    class 入参类(入参基类):
        def __init__(self):
            # 初始化基类
            super().__init__(接口说明='这个脚本是用来演示如何使用【入参基类】来管理传入参数的')
    
            # 添加需要接收的参数
            self._添加参数('a', int, '这是 a 参数, 请输入一个数字', 0)
            self._添加参数('b', int, '这是 b 参数, 请输入一个数字', 0)
            self._添加参数('运算', ['和', '差'], '请输入运算形式', '和')
    
        # region 访问器
        @property
        def a(self) -> int:
            if 'a' in self._参数字典:
                return self._参数字典['a'].else:
                return 0
    
        @a.setter
        def a(self,: int):
            if 'a' in self._参数字典:
                if type() in [int, float]:
                    self._参数字典['a'].= int()
    
        @property
        def b(self) -> int:
            if 'b' in self._参数字典:
                return self._参数字典['b'].else:
                return 0
    
        @b.setter
        def b(self,: int):
            if 'b' in self._参数字典:
                if type() in [int, float]:
                    self._参数字典['b'].= int()
    
        @property
        def 运算(self) -> str:
            if '运算' in self._参数字典:
                return self._参数字典['运算'].else:
                return ''
    
        @运算.setter
        def 运算(self,: str):
            if '运算' in self._参数字典:
                self._参数字典['运算'].= str()
        # endregion
    
    
    if __name__ == '__main__':
        # 实例化入参类
        入参 = 入参类()
    
        # 解析命令行参数
        入参.解析入参()
    
        # 打印获取到的参数
        print(入参.a)
        print(入参.b)
        print(入参.运算)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    如此优雅的命令行参数解析和管理方式,我相信你从来没有见过,独此一份了.

    效率支持

    上面的代码中,我们看到我们为每一个参数写一个 property 的接口,好麻烦啊.优雅个毛线.
    no!no!no! 你能想到的需求,作者当然要支持上了.

    👇下面的代码中, 我们通过 入参.转换为属性范式() 将每一个命令行参数自动生成其对应的访问器接口,并且自动送到您的粘贴板里,您唯一需要做的就是 ctrl-V, 惊不惊喜? 意不意外?

    # -*- coding:UTF-8 -*-
    
    # region 引入必要依赖
    from DebugInfo.DebugInfo import *
    
    # endregion
    
    class 入参类(入参基类):
        def __init__(self):
            # 初始化基类
            super().__init__(接口说明='这个脚本是用来演示如何使用【入参基类】来管理传入参数的')
    
            # 添加需要接收的参数
            self._添加参数('a', int, '这是 a 参数, 请输入一个数字', 0)
            self._添加参数('b', int, '这是 b 参数, 请输入一个数字', 0)
            self._添加参数('运算', ['和', '差'], '请输入运算形式', '和')
    
    
    if __name__ == '__main__':
        # 实例化入参类
        入参 = 入参类()
    
        # 对每一个命令行参数,生成其对应的 访问器接口
        入参.转换为属性范式()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    👆上面的代码中, 入参.转换为属性范式() 的效果如👇:
    DebugInfo 入参基类 命令行参数访问器接口自动生成效果
    共生成了32行代码,每个参数的setter, getter属性都给你准备好了, ctrl+V 是您唯一需要做的事情了.
    不让告诉我你不知道在哪里 ctrl+V 👈

    如果你不希望命令行参数被setter, 你可以通过函数参数控制生成,如👇

    if __name__ == '__main__':
        # 实例化入参类
        入参 = 入参类()
    
        # 对每一个命令行参数,生成其对应的 访问器接口
        入参.转换为属性范式(setter=False)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    小结

    以上所分享的命令行参数解析+管理的方式,提供自 DebugInfo 模块内的 入参基类, 虽然是基于 argparse 的一个二次封装,但相对于直接使用 argparse, 确实方便和清晰不少.

  • 相关阅读:
    XSS攻击
    Linux基础篇(6)---》常用命令大全(网络命令+关机重启命令)
    Leetcode81. Search in Rotated Sorted Array II
    Linux sz 和 rz安装
    Android Studio 中将自己写的工程作为第三方类库的步骤
    浏览器打开一个网页的全流程
    Lua语法之简单变量
    如何使用 HTML data-属性
    白鲸开源 DataOps 平台加速数据分析和大模型构建
    简单介绍.net core 如何实现跨平台和静态编译(写好的程序无法再被直接反编译源码)的桌面应用程序
  • 原文地址:https://blog.csdn.net/weixin_42148809/article/details/133109928