【问题标题】:Using variable arg names with argparse在 argparse 中使用变量 arg 名称
【发布时间】:2017-07-08 21:28:30
【问题描述】:

我有一个上游系统,它使用不同的 arg 名称调用我的程序。示例:

foo --xyz1 10 --xyz2 25 --xyz3 31

我希望 argparsing 的结果是 xyz = [10, 25, 31]。

我的 args 的名称有一个共同的前缀,但不幸的是至少有一个不同的数字后缀,这也表明了顺序。我也没有固定数量的参数。

有没有办法用 argparse 建模?可以通过某种内置功能组合获得可用的功能,也可以通过覆盖/插入某些自定义解析器处理来获得。

【问题讨论】:

  • action='append' 允许您重用--xyz 标志,并按照给定的顺序收集值,例如'--xyz 10 --xyz 25 --xyz 31'
  • 我需要使用不同的参数名称;这是系统另一部分的要求。所以我需要处理--xyz1、--xyz2、--xyz3等等。
  • 这些变量名称键有多少? 3个这样,还是100个?
  • 提前未知。在实际场景中,它会小于 100……但它可能是 1、2、……N。够了,我真的不想添加 N 个文字 args,而是依赖其中的一些被指定。跨度>
  • 部分解析(docs.python.org/3/library/argparse.html#partial-parsing)然后手动检查未知参数列表以检查它们是否需要前缀?

标签: python command-line-interface argparse


【解决方案1】:

我建议进行一些预处理来实现这一点:

代码:

def get_xyz_cmd_line(xyz_cmd_line):
    # build a generator to iterate the cmd_line
    cmd_line_gen = iter(xyz_cmd_line)

    # we will separate the xyz's from everything else
    xyz = []
    remaining_cmd_line = []

    # go through the command line and extract the xyz's
    for opt in cmd_line_gen:
        if opt.startswith('--xyz'):
            # grab the opt and the arg for it
            xyz.append((opt, cmd_line_gen.next()))
        else:
            remaining_cmd_line.append(opt)

    # sort the xyz's and return all of them as -xyz # -xyz # ... 
    return list(it.chain(*[
        ('--xyz', x[1]) for x in sorted(xyz)])) + remaining_cmd_line 

测试:

import argparse
import itertools as it

parser = argparse.ArgumentParser(description='Get my Option')
parser.add_argument('--an_opt', metavar='N', type=int,
                    help='An option')
parser.add_argument('--xyz', metavar='N', type=int, action='append',
                    help='An option')

cmd_line = "--an_opt 1 --xyz1 10 --xyz3 31 --xyz2 25 ".split()
args = parser.parse_args(get_xyz_cmd_line(cmd_line))
print(args)

输出:

Namespace(an_opt=1, xyz=[10, 25, 31])

使用方法:

名义上,而不是上面示例中的固定cmd_line,这将被调用为:

args = parser.parse_args(get_xyz_cmd_line(sys.argv[1:]))

更新:如果您需要 --xyz=31(即= 分隔符):

那么你需要改变:

# grab the opt and the arg for it
xyz.append((opt, cmd_line_gen.next()))

收件人:

if '=' in opt:
    xyz.append(tuple(opt.split('=', 1)))
else:
    # grab the opt and the arg for it
    xyz.append((opt, cmd_line_gen.next()))

【讨论】:

  • 我喜欢你的解决方案,因为我的解决方案(下面发布依赖于内部实现细节)。但是,我还不知道我是否可以使用它,因为我有一个组件添加了参数定义,并且用户调用了 parse_args,我想让用户不知道该怎么做,甚至不知道要执行哪些 args预处理为。使用我派生的 argparser 和自定义操作,此场景无需任何用户干预即可完成。
【解决方案2】:

这是我做的参考(快速和肮脏的版本),虽然我也喜欢 Stephen Rauch 的回答(所以我将其标记为答案 - 特别是因为我使用内部实现细节作为我的解决方案):

class CustomArgumentsParser(argparse.ArgumentParser):

  def _parse_optional(self, arg_string):
    suffix_index = arg_string.find(':')
    if suffix_index < 0:
      return super(CustomArgumentParser, self)._parse_optional(arg_string)

    original_arg_string = arg_string
    suffix = arg_string[suffix_index + 1:]
    arg_string = arg_string[0:suffix_index]

    option_tuple = super(CustomArgumentParser, self)._parse_optional(arg_string)
    if not option_tuple:
      return option_tuple

    action, option_string, explicit_arg = option_tuple
    if isinstance(action, BuildListAction):
      return action, suffix, explicit_arg
    else:
      self.exit(-1, message='Unknown argument %s' % original_arg_string)


class BuildListAction(argparse.Action):
  def __init__(self,
               option_strings,
               dest,
               nargs=None,
               const=None,
               default=None,
               type=None,
               choices=None,
               required=False,
               help=None,
               metavar=None):
    super(BuildListAction, self).__init__(
      option_strings=option_strings,
      dest=dest,
      nargs=nargs,
      const=const,
      default=default,
      type=type,
      choices=choices,
      required=required,
      help=help,
      metavar=metavar)

  def __call__(self, parser, namespace, values, option_string=None):
    index = int(option_string) - 1

    list = getattr(namespace, self.dest)
    if list is None:
      list = []
      setattr(namespace, self.dest, list)

    if index >= len(list):
      list.extend([self.default] * (index + 1 - len(list)))
    list[index] = values

用法:

argparser = CustomArgumentsParser()
argparser.add_argument('--xyz', type=int, action=BuildListAction)

注意 -- 这支持 --xyz:1, --xyz:2, ... 形式的参数,这与原始问题略有不同。

【讨论】:

    猜你喜欢
    • 2016-03-26
    • 2015-07-11
    • 2017-09-23
    • 1970-01-01
    • 2011-08-19
    • 2023-03-30
    • 2011-11-30
    • 2021-03-06
    • 2013-06-01
    相关资源
    最近更新 更多