Python argparse: Force a list item to be unique
Question
Being able to validate the list items using choices=servers
below is nice.
servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer", "SkeppServer", "HavsServer", "SovServer" ]
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--only', nargs='*', choices=servers, help='Space separated list of case sensitive server names to process')
Is it possible to force an item in the list to be unique, so that no duplicates are allowed?
Solution
The way to properly discard duplicates using argparse
would be to create your own argparse.Action
class that takes care of using set
as suggestted by other answers:
import argparse
class UniqueAppendAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
unique_values = set(values)
setattr(namespace, self.dest, unique_values)
servers = ["ApaServer", "BananServer", "GulServer", "SolServer",
"RymdServer", "SkeppServer", "HavsServer", "SovServer" ]
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--only', nargs='*', choices=servers, action=UniqueAppendAction,
help='Space separated list of case sensitive server names to process')
print parser.parse_args()
Example output:
$ python test.py -o ApaServer ApaServer
Namespace(only=set(['ApaServer']))
OTHER TIPS
I don't think, that you can enforce this with argparse
, but I also don't see any reason to do so. Just document in help
that duplicates are ignored. If the user passes duplicate arguments to --only
, just let her do so, and ignore the duplicate argument when processing the option arguments (e.g. by turing the list into a set()
before processing).
Modifying Michel's answer:
In [1]: x = [5,6,5]
In [2]: x_nodups = list(set(x))
In [3]: x_nodups
Out[3]: [5, 6]
In [4]: x_nodups_michel = dict(map(lambda i: (i,1),x)).keys()
In [5]: x_nodups_michel
Out[5]: [5, 6]
Much shorter.
Here's an excerpt from some code that I use for a similar purpose:
def parse_args(argv):
class SetAction(argparse.Action):
"""argparse.Action subclass to store distinct values"""
def __call__(self, parser, namespace, values, option_string=None):
try:
getattr(namespace,self.dest).update( values )
except AttributeError:
setattr(namespace,self.dest,set(values))
ap = argparse.ArgumentParser(
description = __doc__,
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
)
ap.add_argument('--genes', '-g',
type = lambda v: v.split(','),
action = SetAction,
help = 'select by specified gene')
This is similar in spirit to jcollado's reply, with a modest improvement: multiple options can be specified, with comma separated values, and they are deduped (via set) across all options.
For example:
snafu$ ./bin/ucsc-bed -g BRCA1,BRCA2,DMD,TNFA,BRCA2 -g BRCA1
Namespace(genes=set([u'BRCA1', u'BRCA2', u'DMD', u'TNFA']))
Note that there are two -g args. BRCA2 is specified twice in the first, but appears only once. BRCA1 is specified in the first and second -g opts, but also appears only once.