【问题标题】:How to make python argparse mutually exclusive group arguments without prefix?如何使没有前缀的python argparse互斥组参数?
【发布时间】:2011-12-13 17:41:07
【问题描述】:

Python2.7 argparse 只接受互斥组中的可选参数(前缀):

parser = argparse.ArgumentParser(prog='mydaemon')
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument('--start', action='store_true', help='Starts %(prog)s daemon')
action.add_argument('--stop', action='store_true', help='Stops %(prog)s daemon')
action.add_argument('--restart', action='store_true', help='Restarts %(prog)s daemon')

$ mydaemon -h

usage: mydaemon [-h] (--start | --stop | --restart)

optional arguments:
  -h, --help  show this help message and exit
  --start     Starts mydaemon daemon
  --stop      Stops mydaemon daemon
  --restart   Restarts mydaemon daemon

有没有办法让 argparse 参数表现得像传统的 unix 守护进程控制:

(start | stop | restart) and not (--start | --stop | --restart) ?

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    来自pymotw

    import argparse
    
    parser = argparse.ArgumentParser()
    
    group = parser.add_mutually_exclusive_group()
    group.add_argument('-a', action='store_true')
    group.add_argument('-b', action='store_true')
    
    print parser.parse_args()
    

    输出:

    $ python argparse_mutually_exclusive.py -h  
    usage: argparse_mutually_exclusive.py [-h] [-a | -b]
    
    optional arguments:  
      -h, --help  show this help message and exit  
      -a  
      -b  
    
    $ python argparse_mutually_exclusive.py -a  
    Namespace(a=True, b=False)
    
    $ python argparse_mutually_exclusive.py -b  
    Namespace(a=False, b=True)
    
    $ python argparse_mutually_exclusive.py -a -b  
    usage: argparse_mutually_exclusive.py [-h] [-a | -b]  
    argparse_mutually_exclusive.py: error: argument -b: not allowed with argument -a
    

    版本2

    import argparse
    
    parser = argparse.ArgumentParser()
    
    subparsers = parser.add_subparsers(help='commands')
    
    # A list command
    list_parser = subparsers.add_parser('list', help='List contents')
    list_parser.add_argument('dirname', action='store', help='Directory to list')
    
    # A create command
    create_parser = subparsers.add_parser('create', help='Create a directory')
    create_parser.add_argument('dirname', action='store', help='New directory to create')
    create_parser.add_argument('--read-only', default=False, action='store_true',
                           help='Set permissions to prevent writing to the directory',
                           )
    
    # A delete command
    delete_parser = subparsers.add_parser('delete', help='Remove a directory')
    delete_parser.add_argument('dirname', action='store', help='The directory to remove')
    delete_parser.add_argument('--recursive', '-r', default=False, action='store_true',
                           help='Remove the contents of the directory, too',
                           )
    
    print parser.parse_args(['list', 'a s d', ])
    >>> Namespace(dirname='a s d')
    print parser.parse_args(['list', 'a s d', 'create' ])
    >>> error: unrecognized arguments: create
    

    【讨论】:

      【解决方案2】:

      对于argparse 中的所有功能和选项,我认为您永远不会得到看起来像您想要的“固定”使用字符串。

      也就是说,自从您发布原始帖子以来,您是否查看过子解析器?

      这是一个准系统实现:

      import argparse
      
      parser = argparse.ArgumentParser(prog='mydaemon')
      sp = parser.add_subparsers()
      sp_start = sp.add_parser('start', help='Starts %(prog)s daemon')
      sp_stop = sp.add_parser('stop', help='Stops %(prog)s daemon')
      sp_restart = sp.add_parser('restart', help='Restarts %(prog)s daemon')
      
      parser.parse_args()
      

      使用-h 选项运行它会产生:

      usage: mydaemon [-h] {start,stop,restart} ...
      
      positional arguments:
        {start,stop,restart}
          start               Starts mydaemon daemon
          stop                Stops mydaemon daemon
          restart             Restarts mydaemon daemon
      

      这种方法的一个好处是能够为每个子解析器使用set_defaults 将函数直接连接到参数。我还为stoprestart 添加了一个“优雅”选项:

      import argparse
      
      def my_stop(args):
          if args.gracefully:
              print "Let's try to stop..."
          else:
              print 'Stop, now!'
      
      parser = argparse.ArgumentParser(prog='mydaemon')
      
      graceful = argparse.ArgumentParser(add_help=False)
      graceful.add_argument('-g', '--gracefully', action='store_true', help='tries to terminate the process gracefully')
      sp = parser.add_subparsers()
      sp_start = sp.add_parser('start', help='Starts %(prog)s daemon')
      sp_stop = sp.add_parser('stop', parents=[graceful],
                          description='Stops the daemon if it is currently running.',
                          help='Stops %(prog)s daemon')
      sp_restart = sp.add_parser('restart', parents=[graceful], help='Restarts %(prog)s daemon')
      
      # Hook subparsers up to functions
      sp_stop.set_defaults(func=my_stop)
      
      # Uncomment when my_start() and 
      # my_restart() are implemented
      #
      # sp_start.set_defaults(func=my_start)
      # sp_restart.set_defaults(func=my_restart)
      
      args = parser.parse_args()
      args.func(args)
      

      显示stop 的“帮助”消息:

      $ python mydaemon.py stop -h
      usage: mydaemon stop [-h] [-g]
      
      Stops the daemon if it is currently running.
      
      optional arguments:
        -h, --help        show this help message and exit
        -g, --gracefully  tries to terminate the process gracefully
      

      “优雅地”停止:

      $ python mydaemon.py stop -g
      Let's try to stop...
      

      【讨论】:

      • 但是您没有说明如何确定选择了哪个启动停止或重新启动选项。当我尝试查看参数的 repr 时,没有显示任何子解析器参数。
      • @RonaldoNascimento 我想你的问题在这里得到了回答docs.python.org/3/library/… 只需向下滚动一点,有一个示例说明如何使用set_defaults 解决这个问题
      • @RonaldoNascimento 迟到了,但我认为我的编辑回答了你的问题。 my_stop() 的 set_defaults() 始终存在,但很容易被忽略(即使是我)。罗纳尔多的评论让我明白了你的问题。而且我不确定您使用 repr() 执行的操作没有显示您的预期。
      【解决方案3】:

      听起来您想要一个位置参数而不是互斥选项。您可以使用“选择”来限制可能的可接受选项。

      parser = ArgumentParser()
      parser.add_argument('action', choices=('start', 'stop', 'restart'))
      

      这会产生如下所示的用法行:

      usage: foo.py [-h] {start,stop,restart}
      

      【讨论】:

      • 是的,我看到了,但是选择限制了使用的表达性。我只是在寻找一些摆脱前缀的方法。
      • 你是什么意思“限制使用的表现力”?用户可以在不提供其中之一的情况下运行脚本吗?
      • 当用户发出“mydaemon -h”时,帮助(用法)不像为每个参数使用帮助字符串那样清晰。
      • @AdamWagner 如果用户传递了多个参数怎么办?例如foo.py start stop
      • @SantoshKumar 在你的例子中,'stop' 不是第一个位置参数,所以如果我上面的例子中只定义了一个参数,它会导致一个错误,说它不能识别'stop' (在你给出的例子中)。如果您定义了第二个位置参数,那么“start”将是第一个参数的值,“stop”将是第二个参数的值(无论发生了什么)。
      【解决方案4】:

      以 Adam 的回答为基础...如果您想指定默认值,您始终可以执行以下操作,这样他们就可以有效地将其留空。

      import argparse
      
      ActionHelp = """
          Start = Starts the daemon (default)
          Stop = Stops the daemon
          Restart = Restarts the daemon
          """
      parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
      
      parser.add_argument('action', nargs = '?', choices=('start', 'stop', 'restart'),
          default = 'start', help = ActionHelp)
      
      print parser.parse_args(''.split())
      print
      print parser.parse_args('-h'.split())
      

      将打印:

      Namespace(action='start')
      
      usage: program.py [-h] [{start,stop,restart}]
      
      postional arguments:
          {start,stop,restart}
                            Start = Starts the daemon (default)
                            Stop = Stops the daemon
                            Restart = Restarts the daemon
      
      optional arguments:
          -h, --help        show this help message and exit
      

      【讨论】:

        猜你喜欢
        • 2020-07-29
        • 2016-11-25
        • 2021-12-20
        • 2012-12-04
        • 2013-12-28
        • 2011-06-13
        • 1970-01-01
        • 2020-06-19
        相关资源
        最近更新 更多