正确的方式从ArgumentParser获取允许的参数

问题:从现有的argparse.ArgumentParser对象中访问可能参数的意图/官方方式是什么?

示例:我们假设以下上下文:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo', '-f', type=str)

在这里,我想获得允许参数的以下列表:

['-h', '--foo', '--help', '-f']

我发现了下面的解决方法,它为我做了诀窍

parser._option_string_actions.keys()

但是我不满意它,因为它涉及访问一个未正式记录的_成员。 这项任务的正确选择是什么?


我不认为有一种“更好”的方式来实现你想要的。


如果你真的不想使用_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解析器。 这样一个词典可以将选项名称作为键,将选项配置作为值。 这样做,您可以通过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 ,最后一种方法是我会做的。


这开始是一个笑话的答案,但我学到了一些东西 - 所以我会发布它。

假设,我们知道允许的选项的最大长度。 在这种情况下,这是一个很好的答案:

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

当然这很迂腐,因为这个问题不需要任何特定的运行时间。 所以我会在这里忽略它。 我也会无视,上面的版本会产生一团乱七八糟的输出,因为我们可以轻易摆脱它。

但更重要的是,这种方法检测到以下有趣argparse “功能”:

  • 在OP示例中, argparse也会允许--fo 。 这必须是一个错误。
  • 但更进一步,在OP示例中, argparse还会允许-fo-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行为的唯一答案 - 不管它有可能是错误的。 所以,在这里,绝对无用的完美答案。


    我不得不同意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)
    

    有可能是一个正则表达式替代上述混乱...

    链接地址: http://www.djcxy.com/p/93731.html

    上一篇: Correct way to get allowed arguments from ArgumentParser

    下一篇: Bash loop over files in hdfs directory