美文网首页Python全栈工程师
23.1-argparse模块-ls命令实现-单词统计进阶-配置

23.1-argparse模块-ls命令实现-单词统计进阶-配置

作者: BeautifulSoulpy | 来源:发表于2019-10-16 16:08 被阅读0次

    层次高的人,往往都很低调,因为他们活在自己的世界里;
    层次低的人,往往都很高调,因为他们活在别人的世界里;

    人生本过客,何必千千结,想拥有就去努力,努力过后就不在乎结果。成功是付出的收获,失败也是正常的历练。而所有功名利禄实际是早早晚晚的一场云烟,真正拥有的是自己的心情,何必不能释怀,妄自空折磨自己!


    本章节是大数据的核心代码???

    总结:

    1. 文本文件一般是一行行处理,二进制文件是一次性处理;一行行处理,利用生成器是Python写法;
    2. 函数的封装,函数名一般不大写;
    3. 参数动作 定义了解析器在接收到参数后该如何处理参数,那么 参数类别 就是告诉解析器这个参数的元信息,也就是参数是什么样的。比如,参数是字符串呢?还是布尔类型呢?参数是在几个值中可选的呢?还是可以给定值,等等。

    Python解析命令行读取参数有两种方式:sys.argv和argparse

    argparse

    在工作业务中,有些函数的调用要尽量傻瓜,能够让其他人能够方便地调用,毕竟甲方是爸爸。

    假设我有一个python程序,姑且叫extract_WRF.py,内部代码如下例子一,脚本中的main主函数功能简化为打印出输入的参数,即:

    print(zone,input_source_dir,output_source_dir,features,levels)
    

    参数有zone,input_source_dir,output_source_dir,feature,levels,但是该函数print不能在命令行运行,只能在脚本内部调用,于是我们需要python标准库内的argparse[1]来帮忙

    使用步骤:

    1)import argparse 首先导入模块
    2)parser = argparse.ArgumentParser() 创建一个解析对象
    3)parser.add_argument() 向该对象中添加你要关注的命令行参数和选项
    4)parser.parse_args() 解析命令行

    2. 设置解析器

    第一步要做的就是设置解析器,后续对命令行的解析就依赖于这个解析器,它能够将命令行字符串转换为 Python 对象。通过实例化 argparse.ArgumentParser,给定一些选填参数,我们就可以设置一个解析器:

    import argparse
    parser = argparse.ArgumentParser(
        description='My Cmd Line Program',
    )
    

    对于第2步, 通过help parser ,显示其参数有:

    ArgumentParser -
    - pro --程序名称,默认为 sys.argv[0]
    -usage --用法信息,默认从参数自动获取
    -description -- 描述项目是做什么的
    -epilog --紧随着参数描述(argument)的文本
    -formatter_class --自定义帮助信息的格式
    -prefix_chars -- 命令行的前缀
    - fromfile_prefix_chars -- 额外的参数应该读取的文件的前缀字符集(默认:None)
    - argument_default -- 参数的全局默认值(默认:None)
    - conflict_handler --解决冲突的可选策略
    -add help - 给解析器添加-h/–help 选项(默认: True)

    3.定义参数

    通过 ArgumentParser.add_argument 方法来为解析器设置参数信息,以告诉解析器命令行字符串中的哪些内容应解析为哪些类型的 Python 对象,如:

    # 添加 nums 参数,在使用信息中显示为 num
    # 其类型为 int,且支持输入多个,且至少需要提供一个
    parser.add_argument('nums',  metavar='num', type=int, nargs='+',
                        help='a num for the accumulator')
    # 添加 --sum 参数,该参数被 parser 解析后所对应的属性名为 accumulate
    # 若不提供 --sum,默认值为 max 函数,否则为 sum 函数
    parser.add_argument('--sum', dest='accumulate', action='store_const',
                        const=sum, default=max,
                        help='sum the nums (default: find the max)')
    
    argparse 支持的参数
    3.1 参数动作

    action,也就是 参数动作,究竟是用来做什么的呢?

    想象一下,当我们在命令行输入一串参数后,对于不同类型的参数是希望做不同的处理的。那么 参数动作 其实就是告诉解析器,我们希望对应的参数该被如何处理。比如,参数值是该被存成一个值呢,还是追加到一个列表中?是当成布尔的 True 呢,还是 False?

    参数动作 被分成了如下 8 个类别:
    store —— 保存参数的值,这是默认的参数动作。它通常用于给一个参数指定值,如指定名字:

    >>> parser.add_argument('--name')
    >>> parser.parse_args(['--name', 'Eric'])
    Namespace(name='Eric')
    

    store_const—— 保存被 const 命名的固定值。当我们想通过是否给定参数来起到标志的作用,给定就取某个值,就可以使用该参数动作,如:

    >>> parser.add_argument('--sum', action='store_const', const=sum)
    >>> parser.parse_args(['--sum'])
    Namespace(sum=<built-in function sum>)
    >>> parser.parse_args([])
    Namespace(sum=None)
    

    store_true 和 store_false —— 是 store_const 的特殊情况,用来分别保存 True 和 False。如果为指定参数,则其默认值分别为 False 和 True,如:

    >>> parser.add_argument('--use', action='store_true')
    >>> parser.add_argument('--nouse', action='store_false')
    >>> parser.parse_args(['--use', '--nouse'])
    Namespace(nouse=False, use=True)
    >>> parser.parse_args([])
    Namespace(nouse=True, use=False)
    

    append —— 将参数值追加保存到一个列表中。它常常用于命令行中允许多个相同选项,如:

    >>> parser.add_argument('--file', action='append')
    >>> parser.parse_args(['--file', 'f1', '--file', 'f2'])
    Namespace(file=['f1', 'f2'])
    

    append_const —— 将 const 命名的固定值追加保存到一个列表中(const 的默认值为 None)。它常常用于将多个参数所对应的固定值都保存在同一个列表中,相应的需要 dest 入参来配合,以放在同一个列表中,如:

    不指定 dest 入参,则固定值保存在以参数名命名的变量中

    >>> parser.add_argument('--int', action='append_const', const=int)
    >>> parser.add_argument('--str', action='append_const', const=str)
    >>> parser.parse_args(['--int', '--str'])
    Namespace(int=[<class 'int'>], str=[<class 'str'>])
    

    指定 dest 入参,则固定值保存在 dest 命名的变量中

    >>> parser.add_argument('--int', dest='types', action='append_const', const=int)
    >>> parser.add_argument('--str', dest='types', action='append_const', const=str)
    >>> parser.parse_args(['--int', '--str'])
    Namespace(types=[<class 'int'>, <class 'str'>])
    

    count —— 计算参数出现次数,如:

    >>> parser.add_argument('--increase', '-i', action='count')
    >>> parser.parse_args(['--increas', '--increase'])
    Namespace(increase=2)
    >>>parser.parse_args(['-iii'])
    Namespace(increase=3)
    

    help —— 打印解析器中所有选项和参数的完整帮助信息,然后退出。
    version —— 打印命令行版本,通过指定 version 入参来指定版本,调用后退出。如:

    >>> parser = argparse.ArgumentParser(prog='CMD')
    >>> parser.add_argument('--version', action='version', version='%(prog)s 1.0')
    >>> parser.parse_args(['--version'])
    CMD 1.0
    
    3.2 参数类别

    参数动作 定义了解析器在接收到参数后该如何处理参数,那么 参数类别 就是告诉解析器这个参数的元信息,也就是参数是什么样的。比如,参数是字符串呢?还是布尔类型呢?参数是在几个值中可选的呢?还是可以给定值,等等。 逐一介绍不同类型的参数。

    可变参数列表

    可变参数列表 用来定义一个参数可以有多个值,且能通过 nargs 来定义值的个数。

    nargs=N,N为一个数字,则要求该参数提供 N 个值,如:

    >>> parser.add_argument('--foo', nargs=2)
    >>> print(parser.parse_args(['--foo', 'a', 'b']))
    Namespace(foo=['a', 'b'])
    >>> print(parser.parse_args(['--foo', 'a', 'b', 'c']))
    usage: [-h] [--foo FOO FOO]
    : error: unrecognized arguments: c
    

    nargs=?,则要求改参数提供 0 或 1 个值,如:

    >>> parser.add_argument('--foo', nargs='?')
    >>> parser.parse_args(['--foo'])
    Namespace(foo=None)
    >>> parser.parse_args(['--foo', 'a'])
    Namespace(foo='a')
    >>> parser.parse_args(['--foo', 'a', 'b'])
    usage: [-h] [--foo [FOO]]
    : error: unrecognized arguments: b
    

    nargs=*,则要求改参数提供 0 或多个值,如:

    >>> parser.add_argument('--foo', nargs='*')
    >>> parser.parse_args(['--foo'])
    Namespace(foo=[])
    >>> parser.parse_args(['--foo', 'a'])
    Namespace(foo=['a'])
    >>> parser.parse_args(['--foo', 'a', 'b', 'c', 'd', 'e'])
    Namespace(foo=['a', 'b', 'c', 'd', 'e'])
    

    nargs=+,则要求改参数至少提供 1 个值,如:

    >>> parser.add_argument('--foo', nargs='+')
    >>> parser.parse_args(['--foo', 'a'])
    Namespace(foo=['a'])
    >>> parser.parse_args(['--foo'])
    usage: [-h] [--foo FOO [FOO ...]]
    : error: argument --foo: expected at least one argument
    
    可选参数

    可选参数 顾名思义就是参数是可以加上,或不加上。默认情况下,通过 ArgumentParser.add_argument 添加的参数就是可选参数。

    我们可以通过 - 来指定短参数,也就是名称短参数;也可以通过 -- 来指定长参数,也就是名称长的参数。当然也可以两个都指定。

    可选参数通常用于:用户提供一个参数以及对应值,则使用该值;若不提供,则使用默认值。如:

    >>> parser.add_argument('--name', '-n')
    >>> parser.parse_args(['--name', 'Eric'])  # 通过长参数指定名称
    Namespace(name='Eric')
    >>> parser.parse_args(['-n', 'Eric']) # 通过短参数指定名称
    Namespace(name='Eric')
    >>> parser.parse_args([]) # 不指定则默认为 None
    Namespace(name=None)
    
    参数类型

    参数类型 就是解析器参数值是要作为什么类型去解析,默认情况下是 str 类型。我们可以通过 type 入参来指定参数类型。

    argparse 所支持的参数类型多种多样,可以是 int、float、bool等,比如:

    >>> parser.add_argument('-i', type=int)
    >>> parser.add_argument('-f', type=float)
    >>> parser.add_argument('-b', type=bool)
    >>> parser.parse_args(['-i', '1', '-f', '2.1', '-b', '0'])
    Namespace(b=False, f=2.1, i=1)
    

    更厉害的是,type 入参还可以是可调用(callable)对象。这就给了我们很大的想象空间,可以指定 type=open 来把参数值作为文件进行处理,也可以指定自定义函数来进行类型检查和类型转换。

    作为文件进行处理:

    >>> parser.add_argument('--file', type=open)
    >>> parser.parse_args(['--file', 'README.md'])
    Namespace(b=None, f=None, file=<_io.TextIOWrapper name
    

    使用自定义函数进行处理,入参为参数值,需返回转换后的结果。比如,对于参数 --num,我们希望当其值小于 1 时则返回 1,大于 10 时则返回 10:

    >>> def limit(string):
    ...   num = int(string)
    ...   if num < 1:
    ...     return 1
    ...   if num > 10:
    ...     return 10
    ...   return num
    ...
    >>> parser.add_argument('--num', type=limit)
    >>> parser.parse_args(['--num', '-1'])  # num 小于1,则取1
    Namespace(num=1)
    >>> parser.parse_args(['--num', '15'])  # num 大于10,则取10
    Namespace(num=10)
    >>> parser.parse_args(['--num', '5'])  # num 在1和10之间,则取原来的值
    Namespace(num=5)
    
    参数默认值

    参数默认值 用于在命令行中不传参数值的情况下的默认取值,可通过 default 来指定。如果不指定该值,则参数默认值为 None。

    比如:

    >>> parser.add_argument('-i', default=0, type=int)
    >>> parser.add_argument('-f', default=3.14, type=float)
    >>> parser.add_argument('-b', default=True, type=bool)
    >>> parser.parse_args([])
    Namespace(b=True, f=3.14, i=0)
    
    位置参数

    位置参数 就是通过位置而非是 - 或 -- 开头的参数来指定参数值。

    比如,我们可以指定两个位置参数 x 和 y ,先添加的 x 位于第一个位置,后加入的 y 位于第二个位置。那么在命令行中输入 1 2的时候,分别对应到的就是 x 和 y:

    >>> parser.add_argument('x')
    >>> parser.add_argument('y')
    >>> parser.parse_args(['1', '2'])
    Namespace(x='1', y='2')
    
    可选值

    可选值 就是限定参数值的内容,通过 choices 入参指定。

    有些情况下,我们可能需要限制用户输入参数的内容,只能在预设的几个值中选一个,那么 可选值 就派上了用场。

    比如,指定文件读取方式限制为 read-only 和 read-write:

    >>> parser.add_argument('--mode', choices=('read-only', 'read-write'))
    >>> parser.parse_args(['--mode', 'read-only'])
    Namespace(mode='read-only')
    >>> parser.parse_args(['--mode', 'read'])
    usage: [-h] [--mode {read-only,read-write}]
    : error: argument --mode: invalid choice: 'read' (choose from 'read-only', 'read-w
    
    互斥参数

    互斥参数 就是多个参数之间彼此互斥,不能同时出现。使用互斥参数首先通过 ArgumentParser.add_mutually_exclusive_group 在解析器中添加一个互斥组,然后在这个组里添加参数,那么组内的所有参数都是互斥的。

    比如,我们希望通过命令行来告知乘坐的交通工具,要么是汽车,要么是公交,要么是自行车,那么就可以这么写:

    >>> group = parser.add_mutually_exclusive_group()
    >>> group.add_argument('--car', action='store_true')
    >>> group.add_argument('--bus', action='store_true')
    >>> group.add_argument('--bike', action='store_true')
    >>> parser.parse_args([])  # 什么都不乘坐
    Namespace(bike=False, bus=False, car=False)
    >>> parser.parse_args(['--bus'])  # 乘坐公交
    Namespace(bike=False, bus=True, car=False)
    >>> parser.parse_args(['--bike'])  # 骑自行车
    Namespace(bike=True, bus=False, car=False)
    >>> parser.parse_args(['--bike', '--car'])  # 又想骑车,又想坐车,那是不行的
    usage: [-h] [--car | --bus | --bike]
    : error: argument --car: not allowed with argument --bike
    

    4.解析命令行

    定义好参数后,就可以使用 ArgumenteParser.parse_args 方法来解析一组命令行参数字符串了。

    默认情况下,参数取自 sys.argv[1:],它就是你在命令行敲入的一段命令(不含文件名)所对应的一个字符串列表。比如,若你输入 python3 cmd.py --sum 1 2 3,那么 sys.argsv[1:] 就是 ['--sum', '1', '2', '3']。

    当然,也可以通过 parse_args 入参来指定一组命令行参数字符串:

    args = parser.parse_args(['--sum', '-1', '0', '1'])
    print(args) # 结果:Namespace(accumulate=<built-in function sum>, nums=[-1, 0, 1])
    

    5.业务逻辑

    解析好命令行后,我们就可以从解析结果中获取每个参数的值,进而根据自己的业务需求做进一步的处理。比如,对于上文中所定义的 nums 参数,我们可以通过解析后的结果中的 accumulate 方法对其进行求最大值或求和(取决于是否提供 --sum 参数)。

    result = args.accumulate(args.nums)
    print(result)  # 基于上文的 ['--sum', '-1', '0', '1'] 参数,accumulate 为 sum 函数,其结果为 0
    

    依次增加所需参数,并规定其是否是必须参数、类型、默认值、个数。比如required = True 规定其为必要参数,type=str 定义其为字符串,默认值为‘d02’,帮助信息为’WRF模拟区域d01,d02,d03中的一个‘。

    import argparse
    
    parser = argparse.ArgumentParser(
        prog='ls',
        description='List information about the FILEs.',
        add_help=True)   #  1. 设置一个参数解析器
    # ls/etc
    parser.add_argument('path')     #  2. 定义参数
    parser.add_argument('-l')
    parser.add_argument('-a','--all')
    # parser.add_argument('integers', metavar='N', type=int, nargs='+',
    #                     help='an integer for the accumulator')
    # parser.add_argument('--sum', dest='accumulate', action='store_const',
    #                     const=sum, default=max,
    #                     help='sum the integers (default: find the max)')
    
    args = parser.parse_args(('/venv',))   # 解析命令行
    parser.print_help()  # 打印帮助;
    print(args)
    
    # 4. 业务逻辑
    result = args.accumulate(args.nums)
    print(result)
    #-----------------------------------------------------------------------------------------------------
    C:\Users\Administrator\PycharmProjects\learn\venv\Scripts\python.exe C:/Users/Administrator/PycharmProjects/learn/t2.py
    usage: ls [-h] [-l L] [-a ALL] path
    
    List information about the FILEs.
    
    positional arguments:
      path
    
    optional arguments:
      -h, --help         show this help message and exit
      -l L
      -a ALL, --all ALL
    Namespace(all=None, l=None, path='/venv')
    Process finished with exit code 0
    

    对于 features 参数,我们利用nargs来指定 featuers 参数后面的值有多少个,默认为1,本文例子中定义为*,代表无穷多个,即nargs *来表示如果有该位置参数输入的话,之后所有的输入都将作为该位置参数的值。

    练习3:ls命令实现

    import argparse
    from pathlib import Path
    
    parser = argparse.ArgumentParser(
        prog='ls',
        description='List information about the FILEs.',
        add_help=True)   #获得一个参数解析器
    # ls/etc
    parser.add_argument('path',nargs='?',default='.',help='filepath')
    parser.add_argument('-l',dest='list',action='store_true',help='use a long listing format')
    parser.add_argument('-a','--all',action='store_true',help='do not ignore entries strating with .')
    
    # parser.add_argument('integers', metavar='N', type=int, nargs='+',
    #                     help='an integer for the accumulator')
    # parser.add_argument('--sum', dest='accumulate', action='store_const',
    #                     const=sum, default=max,
    #                     help='sum the integers (default: find the max)')
    
    args = parser.parse_args(('./venv'.split()))   # 分析参数
    
    
    print('---------------------------------')
    parser.print_help()  # 打印帮助;
    print(args)
    print('---------------------------------')
    print(args.all,args.list,args.path)
    
    def listdir(p:str,detail=True):
        path = Path(p)
        for x in path.iterdir():
            if detail:
                st = path.stat()
                print(st)
                #t = _getfiletype(path)
                #mode = _getmodestr(st.st_mode)
                mode = stat.filemode(st.st_mode)
                print(mode,x.name)
            else:
                print(x.name)
    
    
    def _getfiletype(p:Path):
        if p.is_dir():
            return 'd'
        elif p.is_block_device():
            return 'b'
        elif p.is_char_device():
            return 'c'
        elif p.is_fifo():
            return'p'
        elif p.is_socket():
            return 's'
        elif p.is_symlink():
            return 'l'
        else:
            return '-'
    
    modelist = ['r','w','x','r','w','x','r','w','x']
    def _getmodestr(mode:int):
        mode = mode &0o777
        mstr = ''
    
        for i in range(8,-1,-1):
            m = mode >> i & 1
            if m:
                mstr += modelist[8-i]
            else:
                mstr += '-'
        return mstr
    import stat
    
    listdir(args.path)
    #--------------------------------------------------------------
    C:\Users\Administrator\PycharmProjects\learn\venv\Scripts\python.exe C:/Users/Administrator/PycharmProjects/learn/t2.py
    ---------------------------------
    usage: ls [-h] [-l] [-a] [path]
    
    List information about the FILEs.
    
    positional arguments:
      path        filepath
    
    optional arguments:
      -h, --help  show this help message and exit
      -l          use a long listing format
      -a, --all   do not ignore entries strating with .
    Namespace(all=False, list=False, path='./venv')
    ---------------------------------
    False False ./venv
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx Include
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx Lib
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx pyvenv.cfg
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx Scripts
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx t.py
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx t2.py
    os.stat_result(st_mode=16895, st_ino=3096224743906169, st_dev=3203957385, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1570584513, st_mtime=1570584513, st_ctime=1568191868)
    drwxrwxrwx test5
    
    Process finished with exit code 0
    

    练习1: 对sample文件进行不区分大小写的单词统计;
    要去用户可以拍数一些单词的统计,例如a、the、of等不应该出现在具有实际意义的统计中,应当忽略要求,全部代码使用函数封装,并调用完成;

    # 函数封装总结:
    #1. topn输出个数n; 切片[]、计数器方式[count、enumerate ] ;
    
    # 方法1: for循环太多
    
    def makekey(s:str):
        chars = set(r"""!'"#./\()[],*-""")
        key = s.lower()
        ret = []
        
        for i ,c in enumerate(key):
            if c in chars:
                ret.append(" ")
            else:
                ret.apeend(c)
        return "".join(ret).split()
    
    def makekey1(s:str):
        chars = set(r"""!'"#./\()[],*-""")
        key = s.lower()  #影响性能
        ret = []
        start = 0
        
        for i,c in enumerate(key):
            if c in chars:
                if start == i:
                    start += 1
                    continue
                ret.append(key[start:i])
                start = i + 1
        else:
            if start < len(key):
                ret.append(key[start:])
        return ret
    d = {}
    with open('./sample.txt',encoding='utf8') as f:
        for line in f:                       # 优化点:能不能减少循环;
            words = line.split()
            for wordlist in map(makekey1,words):
                for word in wordlist:
                    d[word] = d.get(word,0) + 1
                    
    for k,v in sorted(d.items(), key=lambda item:item[1], reverse=True):
        print(k,v)
    
    
    # 方法2: 
    def makekey1(s:str):
        chars = set(r"""!'"#./\()[],*- """)
        #key = s.lower()  #影响性能
        key = s
        #ret = []
        start = 0
        
        for i,c in enumerate(key):
            if c in chars:
                if start == i:
                    start += 1
                    continue
                #ret.append(key[start:i])
                yield key[start:1]
                start = i + 1
        else:
            if start < len(key):
                #ret.append(key[start:])
                yield key[start:]
        #return ret
        
    def topn(n:int,stopwords = set()):
        d = {}
        
        with open('./sample.txt',encoding='utf8') as f:
            for line in f:           # 一行行读取;
                #words = line.split()
                for word in map(str.lower,makekey1(line)):
                    if word not in stopwords:
                        d[word] = d.get(word,0) + 1
                        
        for i ,(k,v) in enumerate(sorted(d.items(), key=lambda item:item[1], reverse=True)):
            if i == n:break
            yield(k,v)
            
        #yield from sorted(d.items(),key=lambda item:item[1],reverse=True)[:n]
        yield from sorted(d.items(),key=lambda item:item[1],reverse=True)
         
    for x in topn(10):
        print(x)
    
    

    练习2:配置文件转换

    from configparser import ConfigParser
    import json
    
    cfg = ConfigParser()
    cfg.read('test.ini')
    d = {}
    for section in cfg.sections():
        d[section] = dict(cfg.items(section))
    print(d)
    json.dumps(d,'te.json' )
    
    

    相关文章

      网友评论

        本文标题:23.1-argparse模块-ls命令实现-单词统计进阶-配置

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