Pourquoi utiliser **kwargs en python? Quels sont les avantages du monde réel par rapport à l'utilisation d'arguments nommés?

je viens d'un milieu statique langues. Est-ce que quelqu'un peut expliquer (idéalement par un exemple) le monde réel les avantages d'utiliser **kwargs sur les arguments nommés ?

pour moi, cela ne fait que rendre l'appel de fonction plus ambigu. Grâce.

56
demandé sur binary lobster 2009-09-12 22:38:01

8 réponses

exemples du monde réel:

décorateurs - ils sont généralement génériques, de sorte que vous ne pouvez pas spécifier les arguments à l'avance:

def decorator(old):
    def new(*args, **kwargs):
        # ...
        return old(*args, **kwargs)
    return new

endroits où vous voulez faire de la magie avec un nombre inconnu d'arguments de mots clés. L'ORM de Django fait cela, p.ex.:

Model.objects.filter(foo__lt = 4, bar__iexact = 'bar')
36
répondu Cat Plus Plus 2009-09-12 18:50:27

vous pouvez vouloir accepter des arguments nommés presque arbitraires pour une série de raisons -- et c'est ce que le formulaire **kw vous permet de faire.

la raison la plus courante est de passer les arguments directement à une autre fonction que vous enveloppez (les décorateurs sont un cas de cela, mais loin d'être le seul!) -- dans ce cas, **kw relâche le couplage entre le wrapper et le wrappee, car le wrapper n'a pas besoin de savoir ou de se soucier de tous les wrappee argument. Voici une autre raison complètement différente:

d = dict(a=1, b=2, c=3, d=4)

si tous les noms devaient être connus à l'avance, alors évidemment cette approche ne pourrait tout simplement pas exister, non? Et btw, le cas échéant, je préfère beaucoup cette façon de faire un dict dont les clés sont des chaînes littérales à:

d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

simplement parce que ce dernier est assez ponctué-lourd et donc moins lisible.

quand aucune des excellentes raisons pour accepter **kwargs s'applique, alors ne l'acceptez pas: c'est aussi simple que cela. IOW, s'il n'y a pas de bonne raison pour permettre à l'appelant de passer des args nommés extra avec des noms arbitraires, ne laissez pas cela se produire -- évitez simplement de mettre un formulaire **kw à la fin de la signature de la fonction dans la déclaration def .

comme pour en utilisant **kw dans un appel, qui vous permet de rassembler l'ensemble exact des arguments nommés que vous devez passer, chaque avec des valeurs correspondantes, dans un dict, indépendamment d'un seul point d'appel, puis utiliser ce dict à l'appel de point. Comparer:

if x: kw['x'] = x
if y: kw['y'] = y
f(**kw)

à:

if x:
  if y:
    f(x=x, y=y)
  else:
    f(x=x)
else:
  if y:
    f(y=y)
  else:
    f()

Même avec seulement deux possibilités (et de la très simples!), l'absence de **kw rend déjà la seconde option absolument intenable et intolérable -- imaginez comment elle se joue lorsqu'il y a une demi-douzaine de possibilités, peut-être un peu plus riches interaction... sans **kw , la vie serait un enfer absolu dans de telles circonstances!

57
répondu Alex Martelli 2012-07-03 16:53:59

une autre raison pour laquelle vous pourriez vouloir utiliser **kwargs (et *args ) est si vous étendez une méthode existante dans une sous-classe. Vous voulez passer tous les arguments existants sur la méthode superclass, mais vous voulez vous assurer que votre classe continue à fonctionner même si la signature change dans une version future:

class MySubclass(Superclass):
    def __init__(self, *args, **kwargs):
        self.myvalue = kwargs.pop('myvalue', None)
        super(MySubclass, self).__init__(*args, **kwargs)
36
répondu Daniel Roseman 2009-09-12 19:15:33

il y a deux cas communs:

D'abord: vous enveloppez une autre fonction qui prend un certain nombre d'argument de mot-clé, mais vous allez juste les passer le long:

def my_wrapper(a, b, **kwargs):
    do_something_first(a, b)
    the_real_function(**kwargs)

Deuxième: Vous êtes prêt à accepter tout argument mot-clé, par exemple, pour définir les attributs d'un objet:

class OpenEndedObject:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

foo = OpenEndedObject(a=1, foo='bar')
assert foo.a == 1
assert foo.foo == 'bar'
10
répondu Ned Batchelder 2009-09-12 18:56:22

**kwargs sont bons si vous ne connaissez pas à l'avance le nom des paramètres. Par exemple, le dict constructeur utilise pour initialiser les clés du nouveau dictionnaire.

dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)
In [3]: dict(one=1, two=2)
Out[3]: {'one': 1, 'two': 2}
4
répondu Cristian Ciupitu 2009-09-12 18:45:42

voici un exemple, que j'ai utilisé dans CGI Python. J'ai créé une classe qui a pris **kwargs à la fonction __init__ . Cela m'a permis d'émuler le DOM côté serveur avec des classes:

document = Document()
document.add_stylesheet('style.css')
document.append(Div(H1('Imagist\'s Page Title'), id = 'header'))
document.append(Div(id='body'))

le seul problème est que vous ne pouvez pas faire ce qui suit, parce que class est un mot-clé Python.

Div(class = 'foo')

La solution est d'accéder au dictionnaire sous-jacent.

Div(**{'class':'foo'})

Je ne dis pas que ceci est une "bonne" utilisation de la fonctionnalité. Ce que je dis, c'est qu'il y a toutes sortes de façons impardonnables d'utiliser des fonctionnalités comme celle-ci.

2
répondu Imagist 2009-09-12 21:56:05

et voici un autre exemple typique:

MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}."

def proclaim(object_, message, data):
    print(MESSAGE.format(**locals()))
1
répondu ilya n. 2009-09-13 05:30:16

un exemple est l'implémentation de Python-argument-binders , utilisé comme ceci:

>>> from functools import partial
>>> def f(a, b):
...     return a+b
>>> p = partial(f, 1, 2)
>>> p()
3
>>> p2 = partial(f, 1)
>>> p2(7)
8

ça vient des functools.partielle python docs: partiel est relativement équivalent " à cette impl:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc
0
répondu Dustin Getz 2017-05-23 11:46:52