2009-09-12 6 views
51

Je viens d'un arrière-plan en langues statiques. Quelqu'un peut-il expliquer (idéalement par l'exemple) le monde réel avantages d'utiliser ** kwargs sur les arguments nommés?Pourquoi utiliser ** kwargs en python? Quels sont les avantages du monde réel sur l'utilisation des arguments nommés?

Pour moi, il semble seulement rendre l'appel de la fonction plus ambigu. Merci.

+1

Pensez varargs en C - vous pouvez parfois ne sais pas ce que vos arguments seront. –

+10

S'il vous plaît ne pas utiliser l'expression "monde réel" - c'est argumentatif. Il dit "tous les exemples que j'ai vus jusqu'ici sont artificiels et inutiles, mon application est le monde réel, vos exemples sont un monde fantastique." S'il vous plaît changer votre question pour éliminer "monde réel". –

+67

Seul quelqu'un qui aime argumenter penserait que ma question était argumentative. – meppum

Répondre

32

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 initiaux:

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

Lieux où vous voulez faire de la magie avec un nombre inconnu d'arguments. ORM de Django fait que, par exemple:

Model.objects.filter(foo__lt = 4, bar__iexact = 'bar') 
4

**kwargs sont bons si vous ne connaissez pas à l'avance le nom des paramètres. Par exemple, le constructeur dict les 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} 
+1

Il est souvent utilisé si vous voulez passer beaucoup d'arguments à une autre fonction dont vous ne connaissez pas forcément les options.Par exemple, specialplot (a, ** kwargs) peut passer des options de tracé dans kwargs à une fonction de tracé générique qui accepte ces options en tant que paramètres nommés. – Tristan

+1

Bien que je considère ce mauvais style, car il entrave l'introspection. – bayer

0

Un exemple met en œuvre 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 

Ceci est de la documentation python functools.partial: est 'relativement équivalente' partielle ceci implique:

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

ici, puisque 'keywords 'est un dictionnaire, .copy() ne fait que créer un autre pointeur? ce qui signifierait que 'fkeywords' est ajouté au dictionnaire original. Je pense que vous voudriez utiliser copy.deepcopy() pour créer une copie réelle du dictionnaire, non? – aeroNotAuto

+0

* 'currying' * est le nom de la rue pour * 'binding argument partiel' * – smci

51

Vous pouvez 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 fréquente est de passer les arguments directement à une autre fonction que vous êtes en train d'encapsuler (les décorateurs sont un cas de ceci, mais FAR du seul!) - dans ce cas, **kw desserre le couplage entre wrapper et wrappee, car le wrapper n'a pas besoin de connaître ou de se soucier de tous les arguments de la wrappee. Voici une autre, tout autre raison:

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

si tous les noms devaient être connus à l'avance, cette approche ne pouvait pas exister de toute évidence, non? Et d'ailleurs, le cas échéant, je préfère de 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 tout à fait des signes de ponctuation lourds et donc moins lisible.

Lorsque aucune des excellentes raisons d'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 de permettre à l'appelant de passer des arguments supplémentaires avec des noms arbitraires, ne le permettez pas - évitez simplement de placer un formulaire **kw à la fin de la signature de la fonction dans l'instruction def.

Quant à en utilisant**kw dans un appel, qui vous permet de mettre ensemble l'ensemble exact des arguments nommés que vous devez passer, chacun avec des valeurs correspondantes, dans un dict, indépendamment d'un seul point d'appel, puis utilisez que dict au point d'appel unique. Comparez:

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 la plus simple nature), le manque de **kw fait aleady la deuxième option absolument intenable et intolérable - juste Imaginez comment il se joue quand il ya une demi-douzaine de possibilités, peut-être dans une interaction un peu plus riche ... sans **kw, la vie serait un enfer absolu dans de telles circonstances!

+0

+1 - étonnamment utile pour être capable de le faire mais quelque chose d'un changement de paradigme si vous êtes habitué à (disons) java. – ConcernedOfTunbridgeWells

10

Il y a deux cas communs:

Première: Vous achevons une autre fonction qui prend un certain nombre d'arguments de mot-clé, mais vous allez juste pour les transmettre:

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' 
31

une autre raison, vous pouvez 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 de superclasse, mais que vous voulez vous assurer que votre classe continue de fonctionner même si les changements de signature dans une future version:

class MySubclass(Superclass): 
    def __init__(self, *args, **kwargs): 
     self.myvalue = kwargs.pop('myvalue', None) 
     super(MySubclass, self).__init__(*args, **kwargs) 
+4

+1: Faites-le très, très fréquemment. –

2

Voici un exemple, j'ai utilisé dans CGI Python. J'ai créé une classe qui a pris **kwargs à la fonction __init__. Cela m'a permis de reproduire les DOM sur le 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 consiste à accéder au dictionnaire sous-jacent. Je ne dis pas que c'est une utilisation "correcte" de la fonctionnalité. Ce que je dis, c'est qu'il y a toutes sortes de façons imprévues dans lesquelles des caractéristiques comme celle-ci peuvent être utilisées.

1

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())) 
Questions connexes