【问题标题】:Get selected subcommand with argparse使用 argparse 获取选定的子命令
【发布时间】:2011-06-02 07:45:18
【问题描述】:

当我使用带有 python argparse 的子命令时,我可以得到选定的参数。

parser = argparse.ArgumentParser()
parser.add_argument('-g', '--global')
subparsers = parser.add_subparsers()   
foo_parser = subparsers.add_parser('foo')
foo_parser.add_argument('-c', '--count')
bar_parser = subparsers.add_parser('bar')
args = parser.parse_args(['-g', 'xyz', 'foo', '--count', '42'])
# args => Namespace(global='xyz', count='42')

所以args 不包含'foo'。由于可能的全局参数,简单地写sys.argv[1] 是行不通的。如何获取子命令本身?

【问题讨论】:

    标签: python command-line argparse


    【解决方案1】:

    Python docs on argparse sub-commands 的最底部解释了如何执行此操作:

    >>> parser = argparse.ArgumentParser()
    >>> parser.add_argument('-g', '--global')
    >>> subparsers = parser.add_subparsers(dest="subparser_name") # this line changed
    >>> foo_parser = subparsers.add_parser('foo')
    >>> foo_parser.add_argument('-c', '--count')
    >>> bar_parser = subparsers.add_parser('bar')
    >>> args = parser.parse_args(['-g', 'xyz', 'foo', '--count', '42'])
    >>> args
    Namespace(count='42', global='xyz', subparser_name='foo')
    

    您也可以使用我找到的示例上面引用的set_defaults() 方法。

    【讨论】:

    • 我也想加required=True强制用户使用子命令。
    • 注意,可能有一个不明显的问题:如果执行.add_argument('--subparser_name'),即添加一个名称与dest匹配的参数,则该参数的值(如果通过) 将覆盖解析器的名称。我认为argparse确实应该检测到这种有问题的配置,但不幸的是它不是。
    【解决方案2】:

    ArgumentParser.add_subparsersdest 形式参数描述为:

    dest - 将存储子命令名称的属性名称;默认None,不存储任何值

    在下面使用子解析器的简单任务函数布局示例中,选定的子解析器位于parser.parse_args().subparser

    import argparse
    
    
    def task_a(alpha):
        print('task a', alpha)
    
    
    def task_b(beta, gamma):
        print('task b', beta, gamma)
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers(dest='subparser')
    
        parser_a = subparsers.add_parser('task_a')
        parser_a.add_argument(
            '-a', '--alpha', dest='alpha', help='Alpha description')
    
        parser_b = subparsers.add_parser('task_b')
        parser_b.add_argument(
            '-b', '--beta', dest='beta', help='Beta description')
        parser_b.add_argument(
            '-g', '--gamma', dest='gamma', default=42, help='Gamma description')
    
        kwargs = vars(parser.parse_args())
        globals()[kwargs.pop('subparser')](**kwargs)
    

    【讨论】:

      【解决方案3】:

      只是想发布这个答案,因为这在我最近的一些工作中非常方便。此方法使用装饰器(尽管不与传统的 @ 语法一起使用),如果推荐的 set_defaults 已与子解析器一起使用,则特别方便。

      import argparse
      from functools import wraps
      import sys
      
      def foo(subparser):
          subparser.error('err')
      
      def bar(subparser):
          subparser.error('err')
      
      def map_subparser_to_func(func, subparser):
          @wraps(func)
          def wrapper(*args, **kwargs):
              return func(subparser, *args, **kwargs)
          return wrapper
      
      parser = argparse.ArgumentParser()
      subparsers = parser.add_subparsers()
      
      foo_parser = subparsers.add_parser('foo')
      foo_parser.set_defaults(func = map_subparser_to_func(foo, foo_parser))
      
      bar_parser = subparsers.add_parser('bar')
      bar_parser.set_defaults(func = map_subparser_to_func(bar, bar_parser))
      
      args = parser.parse_args(sys.argv[1:])
      args.func()
      

      map_subparser_to_func 函数可以修改为将子解析器设置为 wrapper 函数内部的某个类属性或全局变量,而不是直接传递它,也可以重新设计为函数的常规装饰器,尽管这样会需要添加另一层。

      这样就可以直接引用对象了。

      【讨论】:

        猜你喜欢
        • 2014-03-08
        • 2016-05-02
        • 2011-09-15
        • 1970-01-01
        • 2020-04-22
        • 2017-11-01
        • 2019-12-25
        • 2014-08-21
        • 2014-11-30
        相关资源
        最近更新 更多