【问题标题】:Does argparse (python) support mutually exclusive groups of arguments?argparse (python) 是否支持互斥的参数组?
【发布时间】:2011-06-13 19:45:45
【问题描述】:

如果我有参数'-a', '-b', '-c', '-d',使用add_mutually_exclusive_group() 函数,我的程序将只需要使用其中一个。有没有办法把它结合起来,让程序只接受'-a 999 -b 999''-c 999 -d 999'

编辑:添加一个简单的程序更清晰:

>>> parser = argparse.ArgumentParser()
>>> group = parser.add_mutually_exclusive_group()
>>> group.add_argument('-a')
>>> group.add_argument('-b')
>>> group.add_argument('-c')
>>> group.add_argument('-d')

那么只能调用./app.py -a | ./app.py -b | ./app.py -c | ./app.py -d。是否可以将 argparse 分组排除组,以便只调用 ./app.py -a .. -b .. | ./app.py -c .. -d ..

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    编辑:没关系。因为argparse 在调用group.add_argument 时做出了必须创建选项的可怕选择。那不会是我的设计选择。如果您迫切需要此功能,可以尝试使用ConflictsOptionParser

    # exclusivegroups.py
    import conflictsparse
    
    parser = conflictsparse.ConflictsOptionParser()
    a_opt = parser.add_option('-a')
    b_opt = parser.add_option('-b')
    c_opt = parser.add_option('-c')
    d_opt = parser.add_option('-d')
    
    import itertools
    compatible_opts1 = (a_opt, b_opt)
    compatible_opts2 = (c_opt, d_opt)
    exclusives = itertools.product(compatible_opts1, compatible_opts2)
    for exclusive_grp in exclusives:
        parser.register_conflict(exclusive_grp)
    
    
    opts, args = parser.parse_args()
    print "opts: ", opts
    print "args: ", args
    

    因此当我们调用它时,我们可以看到我们得到了想要的效果。

    $ python exclusivegroups.py -a 1 -b 2
    opts:  {'a': '1', 'c': None, 'b': '2', 'd': None}
    args:  []
    $ python exclusivegroups.py -c 3 -d 2
    opts:  {'a': None, 'c': '3', 'b': None, 'd': '2'}
    args:  []
    $ python exclusivegroups.py -a 1 -b 2 -c 3
    Usage: exclusivegroups.py [options]
    
    exclusivegroups.py: error: -b, -c are incompatible options.
    

    警告消息不会告诉您'-a''-b''-c' 不兼容,但是可以制作更合适的错误消息。下面是较旧的错误答案。

    旧编辑: [这个编辑是错误的,虽然如果argparse 以这种方式工作,这不是一个完美的世界吗?] 我之前的回答实际上是不正确,您应该可以通过为每个互斥选项指定一组来使用argparse 执行此操作。我们甚至可以使用itertools 来概括这个过程。并且让我们不必显式输入所有组合:

    import itertools
    compatible_opts1 = ('-a', '-b')
    compatible_opts2 = ('-c', '-d')
    exclusives = itertools.product(compatible_opts1, compatible_opts2)
    for exclusive_grp in exclusives:
        group = parser.add_mutually_exclusive_group()
        group.add_argument(exclusive_grp[0])
        group.add_argument(exclusive_grp[1])
    

    【讨论】:

    • bugs.python.org/issue10984 有一个补丁,可让您将一个参数放入多个互斥组中。这样做很容易改变。使用重叠的组来产生有意义的用法更复杂。
    【解决方案2】:

    我自己偶然发现了这个问题。从我对 argparse 文档的阅读来看,似乎没有一种简单的方法可以在 argparse 中实现这一点。我考虑过使用 parse_known_args,但这很快就相当于编写了一个特殊用途的 argparse 版本;-)

    也许需要一份错误报告。同时,如果你愿意让你的用户做一点额外的输入,你可以用子组来伪造它(比如 git 和 svn 的参数是如何工作的),例如

        subparsers = parser.add_subparsers()
        p_ab = subparsers.add_parser('ab')
        p_ab.add_argument(...)
    
        p_cd = subparsers.add_parser('cd')
        p_cd.add_argument(...)
    

    不理想,但至少它为您提供了 argparse 的好处,而没有太多丑陋的骇客。我最终取消了开关,只使用带有所需子参数的子解析器操作。

    【讨论】:

      【解决方案3】:

      @hpaulj 的评论中提到的argparse 增强请求在九年多之后仍然开放,所以我认为其他人可能会从我刚刚发现的解决方法中受益。基于增强请求中的this comment,我发现我可以使用以下语法向两个不同的互斥组添加一个选项:

      #!/usr/bin/env python                                                                                                                                                                     
      import argparse
      import os
      import sys
      
      def parse_args():
          parser = argparse.ArgumentParser(
              formatter_class=argparse.ArgumentDefaultsHelpFormatter
          )
      
          parser.add_argument("-d", "--device", help="Path to UART device", default="./ttyS0")
      
          mutex_group1 = parser.add_mutually_exclusive_group()
          mutex_group2 = parser.add_mutually_exclusive_group()
      
          mutex_group1.add_argument(
              "-o",
              "--output-file",
              help="Name of output CSV file",
              default="sensor_data_sent.csv",
          )
      
          input_file_action = mutex_group1.add_argument(
              "-i", "--input-file", type=argparse.FileType("r"), help="Name of input CSV file"
          )
      
          # See: https://bugs.python.org/issue10984#msg219660
          mutex_group2._group_actions.append(input_file_action)
      
          mutex_group2.add_argument(
              "-t",
              "--time",
              type=int,
              help="How long to run, in seconds (-1 = loop forever)",
              default=-1,
          )
      
          # Add missing ']' to usage message
          usage = parser.format_usage()
          usage = usage.replace('usage: ', '')
          usage = usage.replace(']\n', ']]\n')
          parser.usage = usage
      
          return parser.parse_args()
      
      
      if __name__ == "__main__":
          args = parse_args()
          print("Args parsed successfully...")
          sys.exit(0)
      

      这对我的目的来说已经足够了:

      $ ./fake_sensor.py -i input.csv -o output.csv                                                                                                                                             
      usage: fake_sensor.py [-h] [-d DEVICE] [-o OUTPUT_FILE | [-i INPUT_FILE | -t TIME]]
      fake_sensor.py: error: argument -o/--output-file: not allowed with argument -i/--input-file
      
      $ ./fake_sensor.py -i input.csv -t 30         
      usage: fake_sensor.py [-h] [-d DEVICE] [-o OUTPUT_FILE | [-i INPUT_FILE | -t TIME]]
      fake_sensor.py: error: argument -t/--time: not allowed with argument -i/--input-file
      
      $ ./fake_sensor.py -i input.csv
      Args parsed successfully...
      
      $ ./fake_sensor.py -o output.csv
      Args parsed successfully...
      
      $ ./fake_sensor.py -o output.csv -t 30
      Args parsed successfully...
      

      访问argparse 的私有成员当然是相当脆弱的,所以我可能不会在生产代码中使用这种方法。此外,精明的读者可能会注意到该用法消息具有误导性,因为这意味着 -o-i 在不能一起使用时可以一起使用(!)但是,我仅将此脚本用于测试,所以我m 并不过分担心。 (我认为,“真正地”修复使用消息需要很多更多的时间来完成这项任务,但如果你知道一个聪明的技巧,请发表评论。)

      【讨论】:

        【解决方案4】:

        子解析器?

        类似于 unhammer 的答案,但具有更多的用户控制权。注意:我没有实际测试过这个方法,但是理论上它应该可以工作并且具有python的功能。

        您可以创建两个解析器,一个用于两个组中的每一个,并使用条件来执行互斥部分。本质上仅将 argparse 用于部分参数解析。使用这种方法,您也可以超越 unhammer 答案的限制。

        # Python 3
        import argparse
        
        try:
            parser = argparse.ArgumentParser()
            parser.add_argument('-a')
            parser.add_argument('-b')
            args = parser.parse_args
        except argparse.ArgumentError:
            parser = argparse.ArgumentParser()
            parser.add_argument('-c')
            parser.add_argument('-d')
            args = parser.parse_args
        

        【讨论】:

          猜你喜欢
          • 2020-07-29
          • 2021-12-20
          • 2021-08-08
          • 2012-12-04
          • 2013-12-28
          • 2011-06-09
          • 2017-06-26
          • 2017-01-17
          相关资源
          最近更新 更多