2016-03-06 22 views
9

Ho scritto alcuni programmi python da riga di comando e ho utilizzato argparse per farlo. Ho strutturato il mio codice in qualche modo come segue.Rimozione degli argomenti da argparse

def main(arg1, arg2): 
    # magic 
    pass 

if __name__ == '__main__': 
    parser = argparse.ArgumentParser() 
    parser.add_argument('arg1') 
    parser.add_argument('arg2') 

    args = parser.parse_args() 

    main(args.arg1, args.arg2) 

E 'davvero super irritante dover chiamare arg1 e arg2 3 volte. Capisco di doverlo fare due volte.

C'è un modo per trattare lo spazio dei nomi restituito dalla funzione parse_args come una tupla? O ancora meglio come una tupla e una dict per gli arg facoltativi e fare il disimballaggio?

if __name__ == '__main__': 
    parser = argparse.ArgumentParser() 
    parser.add_argument('arg1') 
    parser.add_argument('arg2') 
    parser.add_argument('--opt-arg', default='default_value') 

    args, kwargs = parser.magic_method_call_that_would_make_my_life_amazing() 

    # I get goosebumps just thinking about this 
    main(*args, **kwargs) 

risposta

3

https://docs.python.org/3/library/argparse.html#the-namespace-object

Questa classe è volutamente semplice, basta una sottoclasse oggetto con una rappresentazione di stringa leggibile. Se si preferisce avere vista dict-simile degli attributi, è possibile utilizzare il linguaggio standard di Python, Vars():

>>> 
>>> parser = argparse.ArgumentParser() 
>>> parser.add_argument('--foo') 
>>> args = parser.parse_args(['--foo', 'BAR']) 
>>> vars(args) 
{'foo': 'BAR'} 

noti che uno dei grandi progressi, o cambiamenti, almeno, da optparse a argparse è che gli argomenti posizionali, come i tuoi, sono trattati come opzionali. Entrambi appaiono nell'oggetto argsNamespace. In optparse, i posizionali sono solo gli avanzi dall'analisi delle opzioni definite. Si potrebbe ottenere lo stesso effetto in argparse da Omissione dei vostri argomenti e utilizzando parse_known_args:

parser = argparse.ArgumentParser() 
args, extras = parser.parse_known_args() 

args è ora uno spazio dei nomi, e extras una lista. Si potrebbe quindi chiamare la funzione come:

myfoo(*extras, **vars(args)) 

Ad esempio:

In [994]: import argparse 
In [995]: def foo(*args, **kwargs): 
    .....:  print(args) 
    .....:  print(kwargs) 
    .....:  
In [996]: parser=argparse.ArgumentParser() 
In [997]: parser.add_argument('-f','--foo') 
Out[997]: _StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None) 
In [998]: args,extras = parser.parse_known_args(['-f','foobar','arg1','arg2']) 
In [999]: args 
Out[999]: Namespace(foo='foobar') 
In [1000]: extras 
Out[1000]: ['arg1', 'arg2'] 
In [1001]: foo(*extras, **vars(args)) 
('arg1', 'arg2') 
{'foo': 'foobar'} 

Quello stesso argparse paragrafo mostra che si può definire il proprio Namespace classe.Non sarebbe difficile definirne uno che si comporta come un dizionario (da utilizzare come **args) e come spazio dei nomi. Tutto ciò che richiede argparse è che funzioni con getattr e setattr.

In [1002]: getattr(args,'foo') 
Out[1002]: 'foobar' 
In [1004]: setattr(args,'bar','ugg') 
In [1005]: args 
Out[1005]: Namespace(bar='ugg', foo='foobar') 

un'altra caratteristica standard di Python mi permette di passare vars(args) come una tupla:

In [1013]: foo(*vars(args).items()) 
(('foo', 'foobar'), ('bar', 'ugg')) 
{} 

Per una risposta simile da gennaio scorso: https://stackoverflow.com/a/34932478/901925

Neatly pass positional arguments as args and optional arguments as kwargs from argpase to a function

Lì ho dare idee su come per separare i 'posizionali' da 'optionals' dopo l'analisi.


Ecco una classe namespace personalizzato che comprende, nella sua API, un mezzo di per sé che ritorna come un dizionario:

In [1014]: class MyNameSpace(argparse.Namespace): 
    ......:  def asdict(self): 
    ......:   return vars(self) 
    ......:  
In [1015]: args = parser.parse_args(['-f','foobar'], namespace=MyNameSpace()) 
In [1016]: args 
Out[1016]: MyNameSpace(foo='foobar') 
In [1017]: foo(**args.asdict()) 
() 
{'foo': 'foobar'} 

Un'altra idea - utilizzare uno del molteplice nargs (2, '*', '+') per l'argomento posizionale. Quindi hai solo un nome da digitare quando lo passi alla tua funzione.

parser.add_argument('pos',nargs='+') 
args = ... 
args.pos # a list, possibly empty 
foo(*args.pos, **vars(args)) 
4

Cosa c'è di sbagliato nel fare

if __name__ == '__main__': 
    # do argparse stuff as above 
    main(args) 

Cioè, perché sei così appeso di dare main() argomenti posizionali?

Per essere onesto, di solito faccio l'analisi analizzando a) [per piccoli script ecc.] All'inizio del modulo, che mi fornisce una variabile che rientra nell'ambito di tutte le funzioni o b) [di solito] all'interno main() se uso il vostro linguaggio:

def parse_arguments(): 
    parser = argparse.ArgumentParser() 
    parser.add_argument('arg1') 
    parser.add_argument('arg2') 
    args = parser.parse_args() 
    return args 

def main(): 
    args = parse_arguments() 
    # do stuff with args.arg1 and args.arg2 
+1

Non voglio dover comprimere gli argomenti in una tupla per la mia funzione principale se l'ho importato/richiamato da un altro pacchetto. Mi piace la chiamata al metodo 'parse_arguments'. Lo amo davvero. –

5

è possibile vedere una domanda simile chiesto here.

Edit: Alla ricerca di un modo che non sarebbe utilizzare un metodo interno, ho trovato this discussion che ha suggerito di utilizzare vars(). Questo funziona abbastanza bene:

import argparse 

def main(arg1, arg2): 
    print arg1, arg2 

if __name__ == '__main__': 
    parser = argparse.ArgumentParser() 
    parser.add_argument('arg1') 
    parser.add_argument('arg2') 

    args = parser.parse_args() 
    main(**vars(args)) 
+0

Questa è una GRANDE risposta .... Preferirei essere turbato dal design della biblioteca piuttosto che affidarsi a qualcosa che non fa parte dell'API pubblica perché sono un weeny e finisco per ultimo nella vita. –

+0

Cheers @BenHoff :-) Puoi [accettare] (http://stackoverflow.com/help/someone-answers) la mia risposta se funziona per te. –

+0

scusami mi manca un _but_ in là. Questa è un'ottima risposta .... ** ma ** Preferisco essere turbato dal design della biblioteca piuttosto che fare affidamento su qualcosa che non fa parte dell'API pubblica perché sono un weeny e finisco per ultimo nella vita. –

Problemi correlati