Python Argparse: Forza un elemento elenco ad essere unico
Domanda
Essere in grado di convalidare gli elementi dell'elenco utilizzando choices=servers
Di seguito è bello.
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')
È possibile forzare un elemento nell'elenco ad essere unico, in modo che non siano consentiti duplicati?
Soluzione
Il modo per scartare correttamente i duplicati usando argparse
Sarebbe creare il tuo argparse.Action
classe che si occupa di usare set
Come suggerito da altre risposte:
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()
Esempio di output:
$ python test.py -o ApaServer ApaServer
Namespace(only=set(['ApaServer']))
Altri suggerimenti
Non credo, che puoi applicare questo con argparse
, ma non vedo nemmeno alcun motivo per farlo. Basta documentare in help
che i duplicati vengono ignorati. Se l'utente passa argomenti duplicati a --only
, lascia che lo faccia e ignori l'argomento duplicato durante l'elaborazione degli argomenti dell'opzione (ad es. Turing dell'elenco in a set()
prima di elaborazione).
Modifica della risposta di Michel:
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]
Molto più breve.
Ecco un estratto di qualche codice che uso per uno scopo simile:
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')
Questo è simile nello spirito alla risposta di Jcollado, con un modesto miglioramento: è possibile specificare più opzioni, con valori separati da virgola e sono dedicate (tramite set) su tutte le opzioni.
Per esempio:
snafu$ ./bin/ucsc-bed -g BRCA1,BRCA2,DMD,TNFA,BRCA2 -g BRCA1
Namespace(genes=set([u'BRCA1', u'BRCA2', u'DMD', u'TNFA']))
Si noti che ci sono due args. BRCA2 è specificato due volte nel primo, ma appare solo una volta. BRCA1 è specificato nel primo e nel secondo Opts, ma appare anche solo una volta.