Passando un dizionario a una funzione come parametri di parole chiave


346

Vorrei chiamare una funzione in Python usando un dizionario.

Ecco un po 'di codice:

d = dict(param='test')

def f(param):
    print(param)

f(d)

Questo stampa {'param': 'test'}ma mi piacerebbe solo stampare test.

Mi piacerebbe che funzionasse in modo simile per più parametri:

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

È possibile?

Risposte:


529

Alla fine l'ho capito da solo. È semplice, mi mancava solo l'operatore ** per decomprimere il dizionario

Quindi il mio esempio diventa:

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

57
se vuoi che questo aiuti gli altri, dovresti riformulare la tua domanda: il problema non era passare un dizionario, quello che volevi era trasformare un dict in parametri di parole chiave
Javier,

11
Vale la pena notare che è anche possibile decomprimere gli elenchi in argomenti posizionali: f2 (* [1,2])
Matthew Trevor,

10
"dereference": il solito termine, in questo contesto Python, è "unpack". :)
mipadi,

2
Questo è fantastico, l'ho usato solo con argparse / __ dict__ per rendere davvero facile eseguire l'argomento della riga di comando analizzando direttamente le opzioni per un oggetto di classe.
Horus,

1
qual è il motivo per cui vorremmo decomprimere un dizionario quando lo si passa come argomento a una funzione?
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

Alcuni dettagli extra che potrebbero essere utili da sapere (domande che avevo dopo aver letto questo e sono andato e testato):

  1. La funzione può avere parametri che non sono inclusi nel dizionario
  2. È possibile non ignorare un parametro che è già presente nel dizionario
  3. Il dizionario non può avere parametri che non sono presenti nella funzione.

Esempi:

Numero 1: la funzione può avere parametri che non sono inclusi nel dizionario

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

Numero 2: non è possibile ignorare un parametro già presente nel dizionario

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

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

Numero 3: il dizionario non può avere parametri che non sono presenti nella funzione.

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

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

Come richiesto nei commenti, una soluzione al numero 3 consiste nel filtrare il dizionario in base agli argomenti delle parole chiave disponibili nella funzione:

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

Un'altra opzione è quella di accettare (e ignorare) kwarg aggiuntivi nella tua funzione:

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

Si noti inoltre che è possibile utilizzare argomenti posizionali, elenchi o tuple nello stesso modo dei kwargs, ecco un esempio più avanzato che incorpora argomenti posizionali e parole chiave:

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}

4
L'uso del disimballaggio con print.format è particolarmente utile. ad esempio:'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Martlark,

Vecchia domanda ma ancora molto rilevante. Grazie per la risposta dettagliata. Conosci qualche modo per aggirare il caso 3? Significa mappare pitonicamente gli elementi del dizionario sui parametri della funzione, quando ci sono più elementi nel dizionario di quanti siano i parametri?
spencer,

2
@spencer è stata aggiunta una soluzione alla risposta.
David Parks,

33

In Python, questo si chiama "spacchettamento", e puoi trovarne un po 'nel tutorial . La sua documentazione fa schifo, concordo, soprattutto per quanto sia incredibilmente utile.


20
È meglio copiare il contenuto pertinente del collegamento nella risposta, piuttosto che fare affidamento sul collegamento che sopravvive fino alla fine dei tempi.
Richard,

3
@Richard è una profonda opinione filosofica sul web, con la quale non potrei essere più in disaccordo! Ahimè, mi manca lo spazio in questo margine qui per condividere la mia meravigliosa prova ...
llimllib,

@llimllib, allora dovrò chiedere al Dr. Wiles!
Richard,

6

Ecco qui - funziona con qualsiasi altro iterabile:

d = {'param' : 'test'}

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

f(d)

Sembra che le persone stiano effettuando il downvoting di questo dato che ha risposto alla domanda originale, non alla domanda riformulata. Suggerisco di rimuovere questo post ora.
dotancohen,

@dotancohen no non è mai stato corretto, fallisce il secondo blocco di codice che era sempre con la domanda. Ci è voluto troppo alla lettera, la stampa era un esempio.
Dave Hillier,

Risponde alla domanda però, semplicemente non lo fa tramite decompressione del dizionario. Il suo approccio è perfettamente valido in base alla domanda posta.
Natecat,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.