Passer un dictionnaire à une fonction en tant que paramètres de mot-clé

346

Je voudrais appeler une fonction en python à l'aide d'un dictionnaire.

Voici du code:

d = dict(param='test')

def f(param):
    print(param)

f(d)

Ceci imprime {'param': 'test'} mais j'aimerais qu'il imprime test.

J'aimerais qu'il fonctionne de la même manière pour plus de paramètres:

d = dict(p1=1, p2=2)
def f2(p1, p2):
    print(p1, p2)
f2(d)

Est-ce possible?

Dave Hillier
la source

Réponses:

528

Je l'ai compris pour moi à la fin. C'est simple, il me manquait juste l'opérateur ** pour déballer le dictionnaire

Mon exemple devient donc:

d = dict(p1=1, p2=2)
def f2(p1,p2):
    print p1, p2
f2(**d)
Dave Hillier
la source
57
si vous voulez que cela aide les autres, vous devriez reformuler votre question: le problème ne passait pas un dictionnaire, ce que vous vouliez était de transformer un dict en paramètres de mot-clé
Javier
11
Il convient de noter que vous pouvez également décompresser des listes en arguments positionnels: f2 (* [1,2])
Matthew Trevor
10
"dereference": le terme usuel, dans ce contexte Python, est "unpack". :)
mipadi
2
C'est génial, il suffit de l'utiliser avec argparse / __ dict__ pour simplifier l'analyse d'arguments en ligne de commande directement dans les options d'un objet de classe.
Horus
1
quelle est la raison pour laquelle nous voudrions décompresser un dictionnaire en le passant comme argument à une fonction?
Mona Jalal
128
In[1]: def myfunc(a=1, b=2):
In[2]:    print(a, b)

In[3]: mydict = {'a': 100, 'b': 200}

In[4]: myfunc(**mydict)
100 200

Quelques détails supplémentaires qui pourraient être utiles à savoir (questions que j'avais après avoir lu ceci et qui sont allées et testées):

  1. La fonction peut avoir des paramètres qui ne sont pas inclus dans le dictionnaire
  2. Vous ne pouvez pas remplacer un paramètre qui est déjà dans le dictionnaire
  3. Le dictionnaire ne peut pas avoir de paramètres qui ne figurent pas dans la fonction.

Exemples:

Numéro 1: la fonction peut avoir des paramètres qui ne sont pas inclus dans le dictionnaire

In[5]: mydict = {'a': 100}
In[6]: myfunc(**mydict)
100 2

Numéro 2: vous ne pouvez pas remplacer un paramètre qui est déjà dans le dictionnaire

In[7]: mydict = {'a': 100, 'b': 200}
In[8]: myfunc(a=3, **mydict)

TypeError: myfunc() got multiple values for keyword argument 'a'

Numéro 3: Le dictionnaire ne peut pas avoir de paramètres qui ne sont pas dans la fonction.

In[9]:  mydict = {'a': 100, 'b': 200, 'c': 300}
In[10]: myfunc(**mydict)

TypeError: myfunc() got an unexpected keyword argument 'c'

Comme demandé dans les commentaires, une solution au numéro 3 consiste à filtrer le dictionnaire en fonction des arguments de mots clés disponibles dans la fonction:

In[11]: import inspect
In[12]: mydict = {'a': 100, 'b': 200, 'c': 300}
In[13]: filtered_mydict = {k: v for k, v in mydict.items() if k in [p.name for p in inspect.signature(myfunc).parameters.values()]}
In[14]: myfunc(**filtered_mydict)
100 200

Une autre option consiste à accepter (et à ignorer) les kwargs supplémentaires dans votre fonction:

In[15]: def myfunc2(a=None, **kwargs):
In[16]:    print(a)

In[17]: mydict = {'a': 100, 'b': 200, 'c': 300}

In[18]: myfunc2(**mydict)
100

Remarquez plus loin que vous pouvez utiliser des arguments positionnels et des listes ou des tuples de la même manière que les kwargs, voici un exemple plus avancé incorporant à la fois des arguments positionnels et des arguments de mots clés:

In[19]: def myfunc3(a, *posargs, b=2, **kwargs):
In[20]:    print(a, b)
In[21]:    print(posargs)
In[22]:    print(kwargs)

In[23]: mylist = [10, 20, 30]
In[24]: mydict = {'b': 200, 'c': 300}

In[25]: myfunc3(*mylist, **mydict)
10 200
(20, 30)
{'c': 300}
David Parks
la source
4
L'utilisation du déballage avec print.format est particulièrement utile. par exemple:'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Martlark
Vieille question mais toujours très pertinente. Merci pour la réponse détaillée. Connaissez-vous des moyens de contourner le cas 3? Signification mapper de manière pythonique les éléments du dictionnaire aux paramètres de la fonction, lorsqu'il y a plus d'éléments dans le dictionnaire qu'il n'y a de paramètres?
spencer le
2
@spencer une solution a été ajoutée à la réponse.
David Parks
33

En python, cela s'appelle "décompresser", et vous pouvez en trouver un peu dans le tutoriel . La documentation est nul, je suis d'accord, surtout à cause de son utilité fantasmatique.

llimllib
la source
20
Il est préférable de copier le contenu pertinent du lien dans votre réponse, plutôt que de compter sur le lien survivant jusqu'à la fin des temps.
Richard
3
@Richard c'est une opinion philosophique profonde sur le web, avec laquelle je ne pourrais pas être plus en désaccord! Hélas, je manque d'espace dans cette marge pour partager ma merveilleuse preuve ...
llimllib
@llimllib, je devrai alors demander au Dr Wiles!
Richard
6

Et voilà - fonctionne comme n'importe quel autre itérable:

d = {'param' : 'test'}

def f(dictionary):
    for key in dictionary:
        print key

f(d)
Patrick Harrington
la source
Il semble que les gens votent contre cela car il a répondu à la question d'origine, pas à la question reformulée. Je suggère de supprimer ce post maintenant.
dotancohen du
@dotancohen non il n'a jamais été correct, il échoue au deuxième bloc de code qui était toujours à la question. Il l'a pris trop à la lettre, l'impression en était un exemple.
Dave Hillier
Il répond cependant à la question, il ne le fait tout simplement pas via le déballage du dictionnaire. Son approche est parfaitement valable sur la base de la question posée.
Natecat