supposons que j'ai le module myscript.py; Ce module est un code de production et est souvent appelé %dir%>python myscript.py foo bar
.
Je veux l'étendre pour prendre des arguments de mots clés. Je sais que je peux prendre ces arguments en utilisant le script ci-dessous, mais malheureusement, il faudrait l'appeler en utilisant
%dir%>python myscript.py main(foo, bar)
.
Je sais que je peux utiliser le module argparse
, mais je ne sais pas comment le faire.
import sys
def main(foo,bar,**kwargs):
print 'Called myscript with:'
print 'foo = %s' % foo
print 'bar = %s' % bar
if kwargs:
for k in kwargs.keys():
print 'keyword argument : %s' % k + ' = ' + '%s' % kwargs[k]
if __name__=="__main__":
exec(''.join(sys.argv[1:]))
Si vous souhaitez passer des arguments de mots clés comme vous le feriez dans la fonction principale, key=value
, vous pouvez le faire comme ceci:
import sys
def main(foo, bar, *args):
print "Called my script with"
print "foo = %s" % foo
print "bar = %s" % bar
for arg in args:
k = arg.split("=")[0]
v = arg.split("=")[1]
print "Keyword argument: %s = %s" % (k, v)
if __name__ == "__main__":
if len(sys.argv) < 3:
raise SyntaxError("Insufficient arguments.")
if len(sys.argv) != 3:
# If there are keyword arguments
main(sys.argv[1], sys.argv[2], *sys.argv[3:])
else:
# If there are no keyword arguments
main(sys.argv[1], sys.argv[2])
Quelques exemples:
$> python my_file.py a b x=4
Called my script with
foo = a
bar = b
Keyword argument: x = 4
$> python my_file.py foo bar key=value
Called my script with
foo = foo
bar = bar
Keyword argument: key = value
Cependant, cela suppose que la clé et la valeur n'ont aucun espace entre elles, key = value
ne fonctionnera pas.
Si vous cherchez --argument
types d'arguments de mots clés, vous devriez utiliser argparse
.
@Moon m'a battu avec une solution similaire, mais je suggérerais de faire l'analyse préalable et de passer le vrai kwargs
:
import sys
def main(foo, bar, **kwargs):
print('Called myscript with:')
print('foo = {}'.format(foo))
print('bar = {}'.format(bar))
for k, v in kwargs.items():
print('keyword argument: {} = {}'.format(k, v))
if __name__=='__main__':
main(sys.argv[1], # foo
sys.argv[2], # bar
**dict(arg.split('=') for arg in sys.argv[3:])) # kwargs
# Example use:
# $ python myscript.py foo bar hello=world 'with spaces'='a value'
# Called myscript with:
# foo = foo
# bar = bar
# keyword argument: hello = world
# keyword argument: with spaces = a value
Tout d'abord, vous ne passerez pas une expression arbitraire Python comme argument. Elle est fragile et dangereuse.
Pour configurer l'analyseur d'arguments, vous définissez les arguments souhaités, puis les analysez pour produire un objet Namespace
qui contient les informations spécifiées par l'appel de ligne de commande.
import argparse
p = argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('bar')
p.add_argument('--add-feature-a', dest='a', action='store_true', default=False)
Dans votre __main__
, vous analyserez les arguments, puis passerez un dictionnaire produit à partir du Namespace
vers main
.
if __name__ == '__main__':
args = p.parse_args()
main(**vars(args))
Ensuite, vous appellerez votre script avec une ligne comme
# foo = "3", bar = "6", a = True
python myscript.py 3 6 --add-feature-a
ou
# foo = "moo", bar="7.7", a = False
python myscript.py moo 7.7
Vous pouvez faire beaucoup plus avec argparse
, mais ceci est un exemple simple pour obtenir la valeur qu'il produit dans main
.
en deux lignes de code, je peux obtenir des arguments et des kwargs que je peux manipuler comme des arguments et des kwargs standard:
import sys
if __name__=='__main__':
argv=argv[1:]
kwargs={kw[0]:kw[1] for kw in [ar.split('=') for ar in argv if ar.find('=')>0]}
args=[arg for arg in argv if arg.find('=')<0]
#and you can the use args and kwargs as so:
if 'reset' in args:
do_some_functions_with_reset()
a_device_var=kwargs.get('device',False):
#or whatever you want to do with args and kwargs
et le résultat est:
$python test.py reset device=foo format=1 bar
->args=['reset','bar']
->kwargs={'device':'foo','format':'1'}
Avec un peu d'introspection, il est possible de configurer ArgumentParser
à partir de la signature d'une fonction, mappant ainsi les paramètres de ligne de commande directement aux arguments de la fonction:
import argparse
import inspect
def myfun(mode, count=1, frobify=False, *files):
print('Doing %s %d times on %s (%sfrobifying)' % (
mode, count, files, '' if frobify else 'not '
))
def funopt(fun, argv=None):
parser = argparse.ArgumentParser()
if hasattr(inspect, 'getfullargspec'):
spec = inspect.getfullargspec(fun)
else:
spec = inspect.getargspec(fun)
num_defaults = len(spec.defaults) if spec.defaults is not None else 0
for i in range(len(spec.args)):
if i < len(spec.args) - num_defaults:
parser.add_argument(spec.args[i])
Elif spec.defaults[i - len(spec.args)] is False:
parser.add_argument('--' + spec.args[i],
default=False, action='store_true')
else:
default = spec.defaults[i - len(spec.args)]
parser.add_argument('--' + spec.args[i],
default=default,
type=type(default))
if spec.varargs is not None:
parser.add_argument(spec.varargs,
nargs='*')
kwargs = vars(parser.parse_args(argv))
args = []
for arg in spec.args:
args += [kwargs[arg]]
if spec.varargs is not None:
args += kwargs[spec.varargs]
fun(*args)
funopt(myfun)
Le résultat:
$ python test.py
usage: test.py [-h] [--count COUNT] [--frobify] mode [files [files ...]]
test.py: error: too few arguments
$ python test.py myaction a b c
Doing myaction 1 times on ('a', 'b', 'c') (not frobifying)
$ python test.py --frobify --count=5 myaction a b c
Doing myaction 5 times on ('a', 'b', 'c') (frobifying)