【问题标题】:Permit argparse global flags after subcommand子命令后允许 argparse 全局标志
【发布时间】:2014-06-11 09:05:52
【问题描述】:

我正在使用 argparse 构建带有子命令的命令:

mycommand [GLOBAL FLAGS] 子命令 [FLAGS]

我希望全局标志在子命令之前或之后都能正常工作。有没有一种不涉及重复代码的干净方法?

例如:

  parser = argparse.ArgumentParser()
  subparsers = parser.add_subparsers(dest='subparser_name')

  parser.add_argument('--disable')    # This flag...

  sp = subparsers.add_parser('compile')
  sp.add_argument('zones', nargs='*')
  sp.add_argument('--disable')        # Is repeated...

  sp = subparsers.add_parser('launch')
  sp.add_argument('zones', nargs='*')
  sp.add_argument('--disable')        # over and over...

我想为许多标志做这个,所以一遍又一遍地重复自己似乎...... unpythonic。

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    这是parents argparse 功能的完美用例:

    有时,多个解析器共享一组共同的参数。相当 除了重复这些参数的定义,一个解析器 带有所有共享参数并传递给 parents= 参数 可以使用 ArgumentParser。

    定义一个基本父 ArgumentParser,添加将在子解析器之间共享的参数。然后,添加子解析器并通过提供 parents 关键字参数将基本解析器设置为父解析器:

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subparser_name')
    
    base_subparser = argparse.ArgumentParser(add_help=False)
    # define common shared arguments
    base_subparser.add_argument('--disable')
    
    sp = subparsers.add_parser('compile', parents=[base_subparser])
    # define custom arguments
    sp = subparsers.add_parser('launch', parents=[base_subparser])
    # define custom arguments
    

    请注意,此处的add_help=False 有助于避免conflicting help argument 出现问题。

    另见:Python argparse - Add argument to multiple subparsers

    【讨论】:

    • “我希望全局标志能够工作无论它们是在子命令之前还是之后。”我相信当在子命令之前提供标志时,此解决方案不起作用命令行。我已经尝试将 base_subparser 作为父级应用到顶级解析器,但这对于 required=True 参数的行为很糟糕。
    【解决方案2】:

    我在您的示例中看到两个问题:

    1) 在解析器和子解析器中都使用“--disable”。 Nested ArgumentParser 处理重叠的dest

    2) 子解析器中重复的参数集。 parents 当然是简化它的一种方法。但是您可以轻松编写自己的代码:

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subparser_name')
    
    parser.add_argument('--disable', dest='main_disable')    # This flag...
    
    for name in ['compile', 'launch']:
        sp = subparsers.add_parser(name)
        sp.add_argument('zones', nargs='*')
        sp.add_argument('--disable', dest=name+'_disable')        # Is repeated...
    

    【讨论】:

      【解决方案3】:

      您要求 argparse 解决方案,但由于您也要求 Pythonic 解决方案,我将特意提出使用包 docopt 的替代方案:

      得到它:

      $ pip install docopt
      

      将您的代码写入mycommand 文件:

      """
      Usage:
          mycommand compile [--disable] <zone>...
          mycommand launch  [--disable] <zone>...
      
      Arguments:
          <zone>  zone name
      
      Options:
        -h --help
        --disable  disable
      
      """
      from docopt import docopt
      
      if __name__ == "__main__":
          args = docopt(__doc__)
          print args
      

      然后从命令行调用它:

      基本帮助(无参数):

      $ python mycommand
      Usage:
          mycommand compile [--disable] <zone>...
          mycommand launch  [--disable] <zone>...
      

      寻求帮助:

      $ python mycommand -h
      Usage:
          mycommand compile [--disable] <zone>...
          mycommand launch  [--disable] <zone>...
      
      Arguments:
          <zone>  zone name
      
      Options:
        -h --help
        --disable  disable
      

      使用编译子命令:

      $ python mycommand compile zoneAlfa zoneBeta
      {'--disable': False,
       '<zone>': ['zoneAlfa', 'zoneBeta'],
       'compile': True,
       'launch': False}
      

      添加标志--禁用:

      $ python mycommand compile --disable zoneAlfa zoneBeta
      {'--disable': True,
       '<zone>': ['zoneAlfa', 'zoneBeta'],
       'compile': True,
       'launch': False}
      

      launch 子命令也可以:

      $ python mycommand launch --disable zoneAlfa zoneBeta
      {'--disable': True,
       '<zone>': ['zoneAlfa', 'zoneBeta'],
       'compile': False,
       'launch': True}
      

      我对参数解析器的使用也是从 argparse 开始的,我讨厌代码的复杂性,这是做某事所需要的。

      后来我变成了plac,这是将python函数转换为非常有用的控制台命令的非常有效的方法。

      不过,我还是受限于要遵循和理解的一套规则,在很多情况下,这些规则对我来说不是很清楚。对于docopt,我很感激,我可以先编写文档字符串,遵循 POSIX 的标准规则,然后在我的代码中使用它。如果您关心参数的验证,我将指导您查看这个出色软件包的示例。

      【讨论】:

        猜你喜欢
        • 2021-08-26
        • 2014-02-12
        • 1970-01-01
        • 2012-08-23
        • 2022-01-16
        • 2012-01-05
        • 2020-04-13
        • 2018-05-01
        相关资源
        最近更新 更多