Pregunta

I have a dictionary which maps human readable values to three different Python specific values. How can the argparse Python module use this dictionary to get me the specific values while the user can choice between the keys.

Currently I have this:

def parse(a):
    values = { "on": True, "off": False, "switch": None }
    parser = argparse.ArgumentParser()
    parser.add_argument("-v", "--value", choices=values, default=None)
    args = parser.parse_args(a)
    print("{}: {}".format(type(args.value), args.value))

>>> parse(['-v', 'on'])
<type 'str'>: on
>>> parse(['-v', 'off'])
<type 'str'>: off
>>> parse(['-v', 'switch'])
<type 'str'>: switch
>>> parse([])
<type 'NoneType'>: None

The problem is, that argparse doesn't return True, False or None if the parameter is given. Is there an easy way to add this feature? Of course I would be able to execute something like this afterwards:

args.value = values[args.value]

This is actually what I'm doing currently, but this doesn't work well with the default value (I would have to check if the value is already None or set the default to "switch"). And as I'm using this multiple times I would have to do that every time.

¿Fue útil?

Solución

The smallest change would be to use

args.value = values.get(args.values)

so you would get None for any entry not in the dict (eg default).

Another option is to misuse the type keyword of argparse:

values = { "on": True, "off": False, "switch": None }
def convertvalues(value):
    return values.get(value)
parser.add_argument('-v','--value',type=convertvalues)

The "type" approach breaks the use of choices as used above, since choices is applied after the conversion. One possibility to preserve your use of choices would be:

def convertvalues(value):
     return values.get(value,value)
parser.add_argument('-v','--value',type=convertvalues,
                                   choices=[True,False,None],
                                   default=None)

In this case convertvalues returns the right values if 'on','off','switch' and None are used and returns the given values if something else was given (eg. 'bla'). Since 'bla' is not in choices, you get the expected error message.

Using "action" with a class derived from argparse.Action instead of type should do the job the smart way, as given in the docs:

class DictAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        value_dict = { "on": True, "off": False, "switch": None }
        setattr(namespace, self.dest, value_dict.get(values))
parser.add_argument('-v','--value',action=DictAction,
                                   choices=['on','off','switch'],
                                   default=None)

Of course this is not perfect, a better solution would overwrite the Acion init to get the dictionary and leave out the hardcoded value_dict.

Otros consejos

I couldn't comment on oekopez' answer, but this change would be make the class available to be used for any dictionary passed to the add_argument action.

class DictAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, self.choices.get(values, self.default))

value_dict = { "on": True, "off": False, "switch": None }
parser.add_argument('-v','--value',action=DictAction,
                               choices=value_dict,
                               default=None)
values = { "on": True, "off": False, "switch": None }
parser.add_argument("-v", "--value", choices=values.keys())
value = values.get(args.value)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top