pasar el argumento ** kwargs a otra función con ** kwargs

152

No entiendo el siguiente ejemplo, digamos que tengo estas funciones:

# python likes
def save(filename, data, **kwargs):
    fo = openX(filename, "w", **kwargs) # <- #1
    fo.write(data)
    fo.close()
# python doesnt like
def save2(filename, data, **kwargs):
    fo = openX(filename, "w", kwargs) # <- #2
    fo.write(data)
    fo.close()

def openX(filename, mode, **kwargs):
    #doing something fancy and returning a file object

¿Por qué es # 1 la solución correcta y # 2 la incorrecta? **kwargses básicamente un dict, así que si quiero pasar el argumento a openX creo que la forma correcta sería sin **y solo dict el dict. Pero a python obviamente no le gusta el segundo y me dice que di 3 argumentos en lugar de 2. Entonces, ¿cuál es la razón detrás de esto?


fuente
55
Me pregunto por qué lo llamas **argsen el código. Esto es posiblemente el peor nombre posible, ya que la gente se confunda con*args
John La Rooy
1
Bueno, realmente nunca uso * args, así que uso ** args ^^, pero bueno, puedo modificarlo.

Respuestas:

155

En el segundo ejemplo, proporciona 3 argumentos: nombre de archivo, modo y un diccionario ( kwargs). Pero Python espera: 2 argumentos formales más argumentos de palabras clave.

Al prefijar el diccionario con '**', descomprime el diccionario kwargsen argumentos de palabras clave.

Un diccionario (tipo dict) es una variable única que contiene pares clave-valor.

Los "argumentos de palabras clave" son parámetros-método clave-valor.

Cualquier diccionario puede descomprimirse en argumentos de palabras clave con el prefijo **durante la llamada a la función.

gecco
fuente
55
Ahora lo entiendo. Pensé que las palabras clave y dict eran lo mismo.
13
"Cualquier diccionario puede expandirse a palabras clave con el prefijo ** durante la llamada a la función". <- eso es genial
1
Ver también: Documentos de Python sobre parámetros de palabras clave y desempaquetar listas de argumentos
dinosaurio
8
Un ejemplo de código real haría esta respuesta considerablemente más clara.
OrangeDog
13

La **sintaxis le dice a Python que recopile argumentos de palabras clave en un diccionario. La save2está pasando como un argumento sin palabras clave (un objeto de diccionario). No openXestá viendo ningún argumento de palabra clave, por lo **argsque no se utiliza. En su lugar, está obteniendo un tercer argumento sin palabras clave (el diccionario). Para arreglar eso, cambie la definición de la openXfunción.

def openX(filename, mode, kwargs):
    pass
Keith
fuente
Gracias, pero también quiero usar openX sin guardar, así que tengo que seguir con las palabras clave. Pensé que pasar palabras clave era básicamente lo mismo que pasar un dict, pero no lo sabía hasta ahora :)
@xMRW No pueden ser lo mismo, ya que también puede pasar un diccionario como parámetro a cualquier función. Tu # 1 es el correcto entonces.
Keith
8

Ampliando la respuesta de @gecco, el siguiente es un ejemplo que le mostrará la diferencia:

def foo(**kwargs):
    for entry in kwargs.items():
        print("Key: {}, value: {}".format(entry[0], entry[1]))

# call using normal keys:
foo(a=1, b=2, c=3)
# call using an unpacked dictionary:
foo(**{"a": 1, "b":2, "c":3})

# call using a dictionary fails because the function will think you are
# giving it a positional argument
foo({"a": 1, "b": 2, "c": 3})
# this yields the same error as any other positional argument
foo(3)
foo("string")

Aquí puede ver cómo funciona desempacar un diccionario y por qué falla el envío de un diccionario real

Reda Drissi
fuente
1

Porque un diccionario es un valor único. Debe usar la expansión de palabras clave si desea pasarla como un grupo de argumentos de palabras clave.

Ignacio Vazquez-Abrams
fuente
lo siento, pero ¿qué es una "expansión de palabras clave"? ¿quiere decir que debería usar dict_var en lugar de ** args y solo usar def func (argumento, dict_var = 0) ... func (1, {1: "a", 2: "b"})
1

Para # 2, los argumentos serán solo un parámetro formal con valor dict, pero no un parámetro de tipo de palabra clave.

Si desea pasar un parámetro de tipo de palabra clave a un argumento de palabra clave, debe especificar ** antes de su diccionario, lo que significa ** argumentos

mira esto para más detalles sobre el uso de ** kw

http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/

Kit Ho
fuente
Entonces, ¿hay una gran diferencia entre ** kwargs y dict?
gracias, siempre me gusta leer más sobre temas que no entiendo.