Pasar un diccionario a una función como parámetros de palabras clave

346

Me gustaría llamar a una función en python usando un diccionario.

Aquí hay un código:

d = dict(param='test')

def f(param):
    print(param)

f(d)

Esto se imprime {'param': 'test'}pero me gustaría que solo se imprima test.

Me gustaría que funcione de manera similar para más parámetros:

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

es posible?

Dave Hillier
fuente

Respuestas:

528

Lo descubrí por mí mismo al final. Es simple, solo me faltaba el operador ** para descomprimir el diccionario

Entonces mi ejemplo se convierte en:

d = dict(p1=1, p2=2)
def f2(p1,p2):
    print p1, p2
f2(**d)
Dave Hillier
fuente
57
si desea que esto ayude a otros, debería reformular su pregunta: el problema no era pasar un diccionario, lo que quería era convertir un dict en parámetros de palabras clave
Javier
11
Vale la pena señalar que también puede descomprimir listas en argumentos posicionales: f2 (* [1,2])
Matthew Trevor
10
"desreferencia": el término habitual, en este contexto de Python, es "desempaquetar". :)
mipadi el
2
Esto es genial, solo lo usé con argparse / __ dict__ para que sea realmente fácil analizar los argumentos de la línea de comandos directamente en las opciones para un objeto de clase.
Horus
1
¿Cuál es la razón por la que nos gustaría desempaquetar un diccionario al pasarlo como argumento a una función?
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

Algunos detalles adicionales que pueden ser útiles para saber (preguntas que tuve después de leer esto y fui y probé):

  1. La función puede tener parámetros que no están incluidos en el diccionario.
  2. Puede no anular un parámetro que ya está en el diccionario
  3. El diccionario no puede tener parámetros que no estén en la función.

Ejemplos:

Número 1: la función puede tener parámetros que no están incluidos en el diccionario

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

Número 2: no puede anular un parámetro que ya está en el diccionario

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

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

Número 3: el diccionario no puede tener parámetros que no estén en la función.

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

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

Como se solicitó en los comentarios, una solución al Número 3 es filtrar el diccionario en función de los argumentos de palabras clave disponibles en la función:

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

Otra opción es aceptar (e ignorar) kwargs adicionales en su función:

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

Observe más allá de que puede usar argumentos posicionales y listas o tuplas de la misma manera que kwargs, aquí hay un ejemplo más avanzado que incorpora argumentos posicionales y de palabras clave:

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
fuente
44
Usar el desempaquetado con print.format es particularmente útil. por ejemplo:'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Martlark
Antigua pregunta pero aún muy relevante. Gracias por la respuesta detallada. ¿Conoces alguna forma de evitar el caso 3? ¿Significado mapear pitónicamente los elementos del diccionario a los parámetros de la función, cuando hay más elementos en el diccionario que parámetros?
Spencer
2
@spencer se ha agregado una solución a la respuesta.
David Parks
33

En python, esto se llama "desempaquetar", y puede encontrar un poco al respecto en el tutorial . La documentación de esto apesta, estoy de acuerdo, especialmente por lo fantásticamente útil que es.

llimllib
fuente
20
Es mejor copiar el contenido relevante del enlace en su respuesta, en lugar de confiar en que el enlace sobreviva hasta el final de los tiempos.
Richard
3
@Richard esa es una opinión filosófica profunda sobre la web, ¡con la cual no podría estar más en desacuerdo! Por desgracia, me falta el espacio en este margen aquí para compartir mi maravillosa prueba ...
llimllib
@llimllib, ¡tendré que preguntarle al Dr. Wiles entonces!
Richard
6

Aquí tienes: funciona con cualquier otro iterable:

d = {'param' : 'test'}

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

f(d)
Patrick Harrington
fuente
Parece que la gente está rechazando esto, ya que respondió la pregunta original, no la pregunta reformulada. Sugiero simplemente eliminar esta publicación ahora.
dotancohen
@dotancohen no, nunca fue correcto, falla el segundo bloque de código que siempre estaba con la pregunta. Lo tomó demasiado literalmente, la impresión fue un ejemplo.
Dave Hillier
Sin embargo, responde la pregunta, simplemente no lo hace a través del desempaquetado del diccionario. Su enfoque es perfectamente válido según la pregunta publicada.
Natecat