web-dev-qa-db-fra.com

Python: Comment puis-je activer l'utilisation de kwargs lors d'un appel à partir de la ligne de commande? (peut-être avec argparse)

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:]))
11

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.

4
Moon Cheesez

@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
18
user94559

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.

6
chepner

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'}
2
David

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)
0
Vladimir Panteleev