El método de clase genera "TypeError: ... obtuvo varios valores para el argumento de palabra clave ..."

129

Si defino un método de clase con un argumento de palabra clave así:

class foo(object):
  def foodo(thing=None, thong='not underwear'):
    print thing if thing else "nothing" 
    print 'a thong is',thong

llamar al método genera un TypeError:

myfoo = foo()
myfoo.foodo(thing="something")

...
TypeError: foodo() got multiple values for keyword argument 'thing'

¿Que esta pasando?

drevicko
fuente
1
Nunca obtendrá una respuesta satisfactoria de por qué explícito selfes mejor que implícito this.
nurettin

Respuestas:

163

El problema es que el primer argumento pasado a los métodos de clase en python es siempre una copia de la instancia de clase en la que se llama al método, normalmente etiquetado self. Si la clase se declara así:

class foo(object):
  def foodo(self, thing=None, thong='not underwear'):
    print thing if thing else "nothing" 
    print 'a thong is',thong

Se comporta como se esperaba.

Explicación:

Sin selfcomo primer parámetro, cuando myfoo.foodo(thing="something")se ejecuta, el foodométodo se llama con argumentos (myfoo, thing="something"). El ejemplo myfoose asigna a thing(ya que thinges el parámetro de primera declarado), pero Python también intentos de asignar "something"a thing, por lo tanto, la excepción.

Para demostrarlo, intente ejecutar esto con el código original:

myfoo.foodo("something")
print
print myfoo

Saldrás como:

<__main__.foo object at 0x321c290>
a thong is something

<__main__.foo object at 0x321c290>

Puede ver que a 'cosa' se le ha asignado una referencia a la instancia 'myfoo' de la clase 'foo'. Esta sección de los documentos explica cómo los argumentos de función funcionan un poco más.

drevicko
fuente
1
Nota: puede obtener el mismo tipo de error si su función def incluye self como primer parámetro, y luego accidentalmente llama a la función también con self como primer parámetro.
Christopher Hunter
48

Gracias por los mensajes instructivos. Solo me gustaría tener en cuenta que si obtiene "TypeError: foodo () tiene múltiples valores para el argumento de palabra clave 'cosa'", también puede ser que esté pasando erróneamente el 'yo' como parámetro cuando llamar a la función (probablemente porque copió la línea de la declaración de clase, es un error común cuando uno tiene prisa).

joe_doe
fuente
77
Eso es lo que me pasó, gracias por agregar a esta respuesta. Este podría ser el error más común que cometa, y es por eso que está recibiendo mi voto a favor.
rdrey
Lo mismo ocurre cuando se sobrecarga un @classmethod, la solución es usar en super().function(...)lugar de <parentclass>.function(cls, ...).
ederag
30

Esto puede ser obvio, pero podría ayudar a alguien que nunca lo haya visto antes. Esto también sucede para las funciones regulares si asigna erróneamente un parámetro por posición y explícitamente por nombre.

>>> def foodo(thing=None, thong='not underwear'):
...     print thing if thing else "nothing"
...     print 'a thong is',thong
...
>>> foodo('something', thing='everything')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foodo() got multiple values for keyword argument 'thing'
woot
fuente
6

simplemente agregue el decorador 'staticmethod' a la función y el problema está solucionado

class foo(object):
    @staticmethod
    def foodo(thing=None, thong='not underwear'):
        print thing if thing else "nothing" 
        print 'a thong is',thong
adi gunawan
fuente
Acabo de recibir este error y esta solución resolvió mi problema. Pero, ¿puede explicar cómo o por qué este decorador resolvió el problema?
Rayo
1
staticmethod detiene el método de recibir self como primer argumento. Entonces, si llama myfoo.foodo(thing="something"), se asignará thing = "something" al primer argumento, en lugar del argumento propio implícito.
danio
también significa que no puede acceder a las variables de clase dentro de la función, a las que generalmente se accede a través deself
drevicko
4

Quiero agregar una respuesta más:

Sucede cuando intenta pasar el parámetro posicional con un orden de posición incorrecto junto con el argumento de la palabra clave en la función de llamada.

there is difference between parameter and argumentpuedes leer en detalle aquí Argumentos y parámetros en python

def hello(a,b=1, *args):
   print(a, b, *args)


hello(1, 2, 3, 4,a=12)

ya que tenemos tres parámetros:

a es parámetro posicional

b = 1 es palabra clave y parámetro predeterminado

* args es un parámetro de longitud variable

así que primero asignamos un parámetro posicional, significa que tenemos que proporcionar valor al argumento posicional en su orden de posición, aquí el orden importa. pero estamos pasando el argumento 1 en el lugar de a en la función de llamada y luego también estamos proporcionando valor a a, tratándolo como argumento de palabra clave. ahora a tiene dos valores:

uno es el valor posicional: a = 1

segundo es el valor de palabras clave que es a = 12

Solución

Tenemos que cambiar hello(1, 2, 3, 4,a=12)a, hello(1, 2, 3, 4,12) por lo que ahora a obtendrá un solo valor posicional que es 1 yb obtendrá el valor 2 y el resto de valores obtendrá * args (parámetro de longitud variable)

Información Adicional

si queremos que * args debería obtener 2,3,4 y a debería obtener 1 yb debería obtener 12

entonces podemos hacer así
def hello(a,*args,b=1): pass hello(1, 2, 3, 4,b=12)

Algo más :

def hello(a,*c,b=1,**kwargs):
    print(b)
    print(c)
    print(a)
    print(kwargs)

hello(1,2,1,2,8,9,c=12)

salida:

1

(2, 1, 2, 8, 9)

1

{'c': 12}
Aaditya Ura
fuente
eso ya está cubierto por la respuesta stackoverflow.com/a/31822875/12663 : su ejemplo tiene el mismo parámetro (a) asignado por posición y también explícitamente por nombre.
danio
3

Este error también puede ocurrir si pasa un argumento de palabra clave para el cual una de las claves es similar (tiene el mismo nombre de cadena) a un argumento posicional.

>>> class Foo():
...     def bar(self, bar, **kwargs):
...             print(bar)
... 
>>> kwgs = {"bar":"Barred", "jokes":"Another key word argument"}
>>> myfoo = Foo()
>>> myfoo.bar("fire", **kwgs)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bar() got multiple values for argument 'bar'
>>> 

"fuego" ha sido aceptado en el argumento 'bar'. Y sin embargo, hay otro argumento 'bar' presente en kwargs.

Tendría que eliminar el argumento de la palabra clave de los kwargs antes de pasarlo al método.

desbloqueame
fuente
1

También esto puede suceder en Django si está utilizando jquery ajax para url que revierte a una función que no contiene el parámetro 'request'

$.ajax({
  url: '{{ url_to_myfunc }}',
});


def myfunc(foo, bar):
    ...
lukeaus
fuente