在 argparse 中使用变量 arg 名称
Using variable arg names with argparse
我有一个上游系统,它使用不同的 arg 名称调用我的程序。示例:
foo --xyz1 10 --xyz2 25 --xyz3 31
我希望 argparsing 的结果是 xyz = [10, 25, 31]。
我的 args 的名称有一个共同的前缀,但不幸的是至少必须有不同的数字后缀,这也表明顺序。我也没有固定数量的参数。
有没有办法用 argparse 对此进行建模?通过内置功能的某种组合,或者通过 overriding/pluging 在某些自定义解析器处理中使用可用的内容。
我会建议一些 pre-processing 来实现这一点:
代码:
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()))
尽管我也喜欢 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, ... 的参数,这与原始问题略有不同。
我有一个上游系统,它使用不同的 arg 名称调用我的程序。示例:
foo --xyz1 10 --xyz2 25 --xyz3 31
我希望 argparsing 的结果是 xyz = [10, 25, 31]。
我的 args 的名称有一个共同的前缀,但不幸的是至少必须有不同的数字后缀,这也表明顺序。我也没有固定数量的参数。
有没有办法用 argparse 对此进行建模?通过内置功能的某种组合,或者通过 overriding/pluging 在某些自定义解析器处理中使用可用的内容。
我会建议一些 pre-processing 来实现这一点:
代码:
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()))
尽管我也喜欢 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, ... 的参数,这与原始问题略有不同。