问题:从现有argparse.ArgumentParser
对象访问可能参数的预期/官方方式是什么?
示例:让我们假设以下背景:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', '-f', type=str)
在这里,我想获得以下允许参数列表:
['-h', '--foo', '--help', '-f']
我找到了以下解决方法,为我提供了诀窍
parser._option_string_actions.keys()
但我对此并不满意,因为它涉及访问未正式记录的_
成员。什么是这项任务的正确选择?
答案 0 :(得分:3)
我认为没有“更好”的方式来实现你想要的目标。
如果您确实不想使用_option_string_actions
属性,则可以处理parser.format_usage()
以检索选项,但是这样做,您将只获得短选项名称。
如果您想要短选项和长选项名称,则可以处理parser.format_help()
。
此过程可以使用非常简单的正则表达式完成:-+\w+
import re
OPTION_RE = re.compile(r"-+\w+")
PARSER_HELP = """usage: test_args_2.py [-h] [--foo FOO] [--bar BAR]
optional arguments:
-h, --help show this help message and exit
--foo FOO, -f FOO a random options
--bar BAR, -b BAR a more random option
"""
options = set(OPTION_RE.findall(PARSER_HELP))
print(options)
# set(['-f', '-b', '--bar', '-h', '--help', '--foo'])
或者你可以先创建一个包含参数解析器配置的字典,然后从中构建argmuent解析器。这样的dictionnary可以将选项名称作为键,将选项配置作为值。这样做,您可以通过使用itertools.chain展平的字典键访问选项列表:
import argparse
import itertools
parser_config = {
('--foo', '-f'): {"help": "a random options", "type": str},
('--bar', '-b'): {"help": "a more random option", "type": int, "default": 0}
}
parser = argparse.ArgumentParser()
for option, config in parser_config.items():
parser.add_argument(*option, **config)
print(parser.format_help())
# usage: test_args_2.py [-h] [--foo FOO] [--bar BAR]
#
# optional arguments:
# -h, --help show this help message and exit
# --foo FOO, -f FOO a random options
# --bar BAR, -b BAR a more random option
print(list(itertools.chain(*parser_config.keys())))
# ['--foo', '-f', '--bar', '-b']
如果我不愿意使用_option_string_actions
,那么最后一种方式就是我会做的。
答案 1 :(得分:1)
这开始是一个笑话答案,但我从那时起就学到了一些东西 - 所以我发布了它。
假设,我们知道允许的选项的最大长度。在这种情况下,这是一个很好的答案:
from itertools import combinations
def parsable(option):
try:
return len(parser.parse_known_args(option.split())[1]) != 2
except:
return False
def test(tester, option):
return any([tester(str(option) + ' ' + str(v)) for v in ['0', '0.0']])
def allowed_options(parser, max_len=3, min_len=1):
acceptable = []
for l in range(min_len, max_len + 1):
for option in combinations([c for c in [chr(i) for i in range(33, 127)] if c != '-'], l):
option = ''.join(option)
acceptable += [p + option for p in ['-', '--'] if test(parsable, p + option)]
return acceptable
当然这非常迂腐,因为这个问题并不需要任何特定的运行时间。所以我在这里忽略它。我也忽略了上述版本产生了一堆混乱的输出,因为one can get rid of it easily。
但更重要的是,这种方法检测到以下有趣的argparse
"功能":
argparse
也允许--fo
。这必须是一个错误。argparse
也允许-fo
(即将foo
设置为o
,没有空格或任何内容。这是有记录和有意的,但我不知道。因此,正确的解决方案会更长一点,看起来像这样(只有parsable
更改,我将省略其他方法):
def parsable(option):
try:
default = vars(parser.parse_known_args(['--' + '0' * 200])[0])
parsed, remaining = parser.parse_known_args(option.split())
if len(remaining) == 2:
return False
parsed = vars(parsed)
for k in parsed.keys():
try:
if k in default and default[k] != parsed[k] and float(parsed[k]) != 0.0:
return False # Filter '-fx' cases where '-f' is the argument and 'x' the value.
except:
return False
return True
except:
return False
摘要:除了所有限制(运行时和固定的最大选项长度)之外,这是唯一正确尊重真实parser
行为的答案 - 无论多么糟糕。所以,在这里,一个绝对没用的完美答案。
答案 2 :(得分:0)
我必须同意Tryph的回答。
不漂亮,但您可以从parser.format_help()
检索它们:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', '-f', type=str)
goal = parser._option_string_actions.keys()
def get_allowed_arguments(parser):
lines = parser.format_help().split('\n')
line_index = 0
number_of_lines = len(lines)
found_optional_arguments = False
# skip the first lines until the section 'optional arguments'
while line_index < number_of_lines:
if lines[line_index] == 'optional arguments:':
found_optional_arguments = True
line_index += 1
break
line_index += 1
result_list = []
if found_optional_arguments:
while line_index < number_of_lines:
arg_list = get_arguments_from_line(lines[line_index])
if len(arg_list) == 0:
break
result_list += arg_list
line_index += 1
return result_list
def get_arguments_from_line(line):
if line[:2] != ' ':
return []
arg_list = []
i = 2
N = len(line)
inside_arg = False
arg_start = 2
while i < N:
if line[i] == '-' and not inside_arg:
arg_start = i
inside_arg = True
elif line[i] in [',',' '] and inside_arg:
arg_list.append(line[arg_start:i+1])
inside_arg = False
i += 1
return arg_list
answer = get_allowed_arguments(parser)
可能有一个正则表达式替代上面的混乱......
答案 3 :(得分:0)
首先关于argparse
文档的说明 - 它基本上是一个如何使用的文档,而不是正式的API。 argparse
所做的标准是代码本身,单元测试(test/test_argparse.py
),以及对后向兼容性的瘫痪问题。
没有'官方'方式访问allowed arguments
,因为用户通常不需要知道(除了阅读help/usage
)。
但是让我用迭代会话中的简单解析器来说明:
In [247]: parser=argparse.ArgumentParser()
In [248]: a = parser.add_argument('pos')
In [249]: b = parser.add_argument('-f','--foo')
add_argument
返回它创建的Action对象。这没有记录,但对任何以交互方式创建解析器的人来说都很明显。
parser
对象有repr
方法,显示主要参数。但它有更多属性,您可以在Ipython中看到vars(parser)
或parser.<tab>
。
In [250]: parser
Out[250]: ArgumentParser(prog='ipython3', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
行动也有repr
; Action子类由action
参数确定。
In [251]: a
Out[251]: _StoreAction(option_strings=[], dest='pos', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
In [252]: b
Out[252]: _StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
vars(a)
等可用于查看所有属性。
键parser
属性为_actions
,是所有已定义操作的列表。这是所有解析的基础。请注意,它包含自动创建的help
操作。看看option_strings
;确定Action是位置还是可选。
In [253]: parser._actions
Out[253]:
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=[], dest='pos',....),
_StoreAction(option_strings=['-f', '--foo'], dest='foo', ...)]
_option_string_actions
是一个字典,从option_strings
映射到Actions(_actions
中出现的相同对象)。这些Action对象的引用会出现在argparse
代码中的所有位置。
In [255]: parser._option_string_actions
Out[255]:
{'--foo': _StoreAction(option_strings=['-f', '--foo'],....),
'--help': _HelpAction(option_strings=['-h', '--help'],...),
'-f': _StoreAction(option_strings=['-f', '--foo'], dest='foo',...),
'-h': _HelpAction(option_strings=['-h', '--help'], ....)}
In [256]: list(parser._option_string_actions.keys())
Out[256]: ['-f', '--help', '-h', '--foo']
请注意,每个-
字符串都有一个键,长或短;但pos
没有任何内容,位置有一个空的option_strings
参数。
如果您希望使用该键列表,请使用它,而不必担心_
。它没有'公共'别名。
我能理解解析help
以发现同样的事情;但是要避免使用“私有”属性需要做很多工作。如果您担心未记录的未记录属性被更改,您还应该担心要更改的帮助格式。这也不是文档的一部分。
help
布局由parser.format_help
控制。 usage
是根据self._actions
中的信息创建的。
for action_group in self._action_groups:
formatter.add_arguments(action_group._group_actions)
(你不想进入action groups
吗?)。
另一种获取option_strings
的方法 - 从_actions
收集它们:
In [258]: [a.option_strings for a in parser._actions]
Out[258]: [['-h', '--help'], [], ['-f', '--foo']]
===================
稍微深入研究代码细节:
parser.add_argument
创建一个Action,然后将其传递给parser._add_action
。这是填充.actions
和action.option_strings
的方法。
self._actions.append(action)
for option_string in action.option_strings:
self._option_string_actions[option_string] = action