如何使用argparse获取选项字符串?

11
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--pattern", help="Pattern file")
args = parser.parse_args()

现在,是否可以从args中获取字符串"--pattern"?我需要这个字符串以便构造一个命令列表传递给Popen,例如Popen(['some_other_program', args.pattern.option_string, args.pattern], ...),而不重复它(并且必须在两个地方维护它)(Popen(['some_other_prog', '--pattern', args.pattern], ...))。

我需要为另一个程序创建一个包装器。一些参数需要通过Popen传递给封装的程序,而其他参数则是包装器所需的。


有没有比以下示例更好的方法?

pass_1 = '--to-be-passed'
parser = argparse.ArgumentParser()
parser.add_argument("-p", pass_1, help="Pass me on")
parser.add_argument("-k", "--arg-for-wrapper")
args = parser.parse_args()

...
process = Popen(['wrapped_program', pass_1, args.pass_1], ...)
... 

将参数保存在变量中的方法并不是很好,因为:

  1. 同时维护短选项和长选项变得困难。
  2. 如果在另一个函数中调用Popen,则需要将这些变量(或其字典)传递给该函数。这似乎是多余的,因为传递给它的args应该已经足够了。

2
为什么不直接将其存储在一个变量中呢?pattern_str = '--pattern',然后...add_argument(... pattern_str...)。这样,只有一个维护点。 - Ami Tavory
听起来你正在尝试在两个独立的程序之间硬编码依赖关系(而且非常死板:“这些程序必须共享选项名称”)。这真的是你想要做的吗? - user1084944
@Hurkyl 是的,这个 Python 程序是另一个程序的包装器。 - user80551
同意@AmiTavory的解决方案是唯一可维护的方式,尝试这样做。也许可以使用一个全局字典,将所有模式作为键和它们的选项标志作为值,以及大量注释来显示为什么将它们提取成全局变量。 - Adam Smith
@AmiTavory argparser解析两种类型的参数 - 一种是特定于Python包装器的参数,另一种是要传递给Popen的参数。拥有变量(用于传递给Popen的参数)而不是特定于包装器的变量似乎有点混乱,尽管这是一个更好的选择。我只是想问是否有更好的方法。 - user80551
3个回答

4
在你的`add_argument`调用中添加一个`dest`。
parser.add_argmument("p", "--pattern", dest="pattern", help="your help text")
args = parser.parse_args()
args = vars(args)

你可以使用 args["pattern"] 引用该模式。

4
argparse--pattern推断出destpattern。因此,添加'dest'是不必要的。args是一个Namespace对象,而不是一个字典。vars(args)将其转换为字典。 - hpaulj
这并没有回答问题。他正在询问如何只在一个地方定义例如 --some-opt,但您没有指示如何从解析器中获取字符串 --some-opt - cjs

4

似乎没有一种简单的方法可以从parser.parse_args()的结果中获取原始选项字符串,但是您可以从parser对象中获取它们。您只需要查看其__dict__,以便在创建后检索parser设置。在您的情况下,您需要_option_string_actions字段。不幸的是,这似乎不受官方支持,因为我找不到专门用于此的ArgumentParser方法,所以可能会有所不同。在Python 3上:

演示:

parser = argparse.ArgumentParser()
parser.add_argument('--foo', '-f', type=int, default=1000, help='intensity of bar')
parser.add_argument('--bar', '-b', type=str, default='bla', help='whatever')
store_action_dict=vars(parser)['_option_string_actions']
print(store_action_dict.keys())    # dict_keys(['--help', '-b', '-f', '-h', '--foo', '--bar'])

具有前导下划线的变量被认为是未公开的。因此,_option_string_actions 是私有变量,可以随时更改。因此,个人建议避免使用这种类型的编码。 - Peter Moore

1

被删除的答案和评论表明有些混淆,不清楚你想要什么。所以我会增加这种混淆。

通常情况下,解析器不会记录选项字符串。但是它提供给 Action__call__ 方法。因此,自定义 Action 类可以保存它。argparse 文档中的 FooAction 自定义类示例说明了这一点。

如果我定义这个操作子类:

In [324]: class PassThru(argparse._StoreAction):
def __call__(self, parser, namespace, values, option_string=None):
    setattr(namespace, self.dest, [values, option_string])

In [324]: p.add_argument('-o','--other',action=PassThru)

选项字符串与值(“-o”或“--other”)一起记录:
In [322]: p.parse_args('-p test -o teseting'.split())
Out[322]: Namespace(other=['teseting', '-o'], pass_me_on='test')

In [323]: p.parse_args('-p test --other teseting'.split())
Out[323]: Namespace(other=['teseting', '--other'], pass_me_on='test')

很明显,option_string和value可以以不同的顺序、在字典中、作为Namespace的单独属性等方式记录。

有其他方法将选项传递给另一个程序,特别是如果包装解析器本身不需要处理这些选项。

argparsesys.argv[1:]获取参数,并且不会更改它。因此,即使您的解析器使用了一些参数,您也可以将该列表全部或部分传递给popen

argparse文档提供了一个示例,在nargs=REMAINDER下解析自己的一些参数,并收集其余参数以传递给另一个程序。这是他们的示例:

>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--foo')
>>> parser.add_argument('command')
>>> parser.add_argument('args', nargs=argparse.REMAINDER)
>>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split()))
Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B')

所以您可以使用类似以下方式调用popen
plist = ['wrapped_program']
plist.extend(args.args)
popen(plist, ...)

使用parse.parse_known_args也可以将未解析的单词收集到一个“extras”列表中。文档的这一部分讨论了将这些字符串传递给另一个程序(就像您正在做的那样)。与REMAINDER情况相反,额外的内容不必是最后一个。
当然,这些只有在此解析器自己不需要--pattern时才有效。如果它进行了解析,则不会出现在REMAINDER或extras中。在这种情况下,您将不得不将其添加回您给popen的列表中。
我会对您的解析器进行微调:
pass_1 = 'passed'  # without the -- or internal -
dpass_` = '--'+pass_
parser = argparse.ArgumentParser()
parser.add_argument("-p", dpass_1, help="Pass me on")
parser.add_argument("-k", "--arg-for-wrapper")
args = parser.parse_args()
process = Popen(['wrapped_program', dpass_1, getattr(args, pass_1)], ...)

另一种选择:

parser = argparse.ArgumentParser()
pass_action = parser.add_argument("-p", '--pass-me-on', help="Pass me on")
parser.add_argument("-k", "--arg-for-wrapper")
args = parser.parse_args()

如果你在 shell 中打印 pass_action,你会得到类似以下的结果:
 _StoreAction(option_strings=['-p', '--pass-me-on'], dest='pass_me_on', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)

因此,您可以从该对象中提取--namedest,如下所示:

process = Popen(['wrapped_program', pass_action.option_strings[-1], getattr(args, pass_action.dest), ...], ...)

你需要查看 sys.argv,以确定使用了哪个选项字符串(长、短或其他)。解析器不会在任何地方记录这一点。
请注意,'--pass-me-on' 产生了 dest='pass_me_on'。将 - 转换为 _ 可能会使从一个字符串推导出另一个字符串变得复杂。
如果你有一个 dest 字符串,你必须使用 getattr 从 args 命名空间中提取它,或使用 vars(args)[dest](字典访问)。
另一个问题是,如果 --pattennargs='+', 那么它的值将是一个列表,而不是一个字符串。当将其合并到 popen 参数列表中时,你需要小心。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接