argparse:将用户输入映射到已定义的常量

YSC*_*YSC 4 python python-2.7 argparse

我想parser.add_argument(...)在我的代码中使用常量定义构建一个to map参数.

假设我有以下内容

import argparse

# Both are the same type
CONST_A = <something>
CONST_B = <otherthing>

parser = argparse.ArgumentParser()
parser.add_argument(...)

# I'd like the following to be true:
parser.parse_args("--foo A".split()).foo == CONST_A
parser.parse_args("--foo B".split()).foo == CONST_B
Run Code Online (Sandbox Code Playgroud)

我能代替...什么呢?


我能做的最好的const是:

import argparse

# Both are the same type
CONST_A = 10
CONST_B = 20

parser = argparse.ArgumentParser()
status_group = parser.add_mutually_exclusive_group(required=True)
status_group.add_argument("-a", const=CONST_A, action='store_const')
status_group.add_argument("-b", const=CONST_B, action='store_const')

# I'd like the following to be true:
print parser.parse_args("-a".split()).a == CONST_A # True
print parser.parse_args("-b".split()).b == CONST_B # True
Run Code Online (Sandbox Code Playgroud)

需要注意的是常量保存到TW odifferent属性ab,女巫适合我不:(

Tad*_*sen 7

最简单的方法是取的优势,type=在选择add_argument@hpaulj做虽然可以用一个工厂函数来概括:

def argconv(**convs):
    def parse_argument(arg):
        if arg in convs:
            return convs[arg]
        else:
            msg = "invalid choice: {!r} (choose from {})"
            choices = ", ".join(sorted(repr(choice) for choice in convs.keys()))
            raise argparse.ArgumentTypeError(msg.format(arg,choices))
    return parse_argument
Run Code Online (Sandbox Code Playgroud)

然后代替...使用type=argconv(A=CONST_A, B=CONST_B):

parser.add_argument("--foo", type=argconv(A=CONST_A, B=CONST_B))
Run Code Online (Sandbox Code Playgroud)

然后一切都会按照你想要的那样在你的例子中运行.


以下是我发布的第一个答案,它仍然有效但并不像上面的解决方案那么简单.

另一种方法是创建一个继承自argparse.ArgumentParser和重写的类,parse_args以便在生成结果时修改结果:

import argparse

class MappedParser(argparse.ArgumentParser):
    mapping = {} #backup if you don't use def_mapping

    def def_mapping(self,**options):
        self.mapping = options

    def parse_args(self,args=None,namespace=None):
        result = argparse.ArgumentParser.parse_args(self,args,namespace)
        for name,options in self.mapping.items(): #by default this is is empty so the loop is skipped
            if name in result:
                key = getattr(result,name)
                if key in options:
                    replace_with = options[key]
                    setattr(result,name,replace_with)
                else:
                    self.error("option {name!r} got invalid value: {key!r}\n must be one of {valid}".format(name=name,key=key,valid=tuple(options.keys())))
                    return #error should exit program but I'll leave this just to be safe.
        return result
Run Code Online (Sandbox Code Playgroud)

这样你的(示例)程序的其余部分将如下所示:

# There is nothing restricting their type.
CONST_A = "<something>"
CONST_B = ["other value", "type is irrelevent"]

parser = MappedParser() #constructor is same

parser.def_mapping(foo={"A":CONST_A, "B":CONST_B})

parser.add_argument("--foo") # and this is unchanged

# the following is now true:
print(parser.parse_args("--foo A".split()).foo is CONST_A)
print(parser.parse_args("--foo B".split()).foo is CONST_B)
#note that 'is' operator works so it is even the same reference

#this gives decent error message
parser.parse_args("--foo INVALID".split())

print("when parser.error() is called the program ends so this never is printed")
Run Code Online (Sandbox Code Playgroud)

添加这样的额外选项:

parser.def_mapping(foo={"A":CONST_A, "B":CONST_B,"C":"third option"})
Run Code Online (Sandbox Code Playgroud)

或者像这样的额外参数:

parser.def_mapping(foo={"A":CONST_A, "B":CONST_B},
                   conv={"int":int,"float":float})
Run Code Online (Sandbox Code Playgroud)

以及未指定的任何添加的参数都是def_mapping独立的,因此很容易实现.