La méthode de classe génère "TypeError:… a obtenu plusieurs valeurs pour l'argument de mot-clé…"

129

Si je définis une méthode de classe avec un argument mot-clé ainsi:

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

l'appel de la méthode génère un TypeError:

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

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

Que se passe-t-il?

Drevicko
la source
2
Vous n'obtiendrez jamais une réponse satisfaisante quant à la raison pour laquelle l'explicite selfest préférable à l'implicite this.
nurettin

Réponses:

164

Le problème est que le premier argument passé aux méthodes de classe en python est toujours une copie de l'instance de classe sur laquelle la méthode est appelée, généralement étiquetée self. Si la classe est déclarée ainsi:

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

il se comporte comme prévu.

Explication:

Sans selfcomme premier paramètre, lors de l' myfoo.foodo(thing="something")exécution, la foodométhode est appelée avec des arguments (myfoo, thing="something"). L'instance myfooest alors affectée à thing(depuis thingle premier paramètre déclaré), mais aussi python tente d'assigner "something"à thing, d' où l'exception.

Pour démontrer, essayez d'exécuter ceci avec le code d'origine:

myfoo.foodo("something")
print
print myfoo

Vous sortirez comme:

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

<__main__.foo object at 0x321c290>

Vous pouvez voir que 'thing' a reçu une référence à l'instance 'myfoo' de la classe 'foo'. Cette section de la documentation explique comment les arguments de fonction fonctionnent un peu plus.

Drevicko
la source
1
à noter: vous pouvez obtenir le même type d'erreur si votre fonction def inclut self comme premier paramètre, puis vous appelez accidentellement la fonction également avec self comme premier paramètre.
Christopher Hunter
48

Merci pour les messages instructifs. Je voudrais juste garder une note que si vous obtenez "TypeError: foodo () a plusieurs valeurs pour l'argument mot-clé 'chose'", il se peut aussi que vous passiez par erreur le 'self' comme paramètre lorsque appeler la fonction (probablement parce que vous avez copié la ligne de la déclaration de classe - c'est une erreur courante quand on est pressé).

joe_doe
la source
7
C'est ce qui m'est arrivé, merci d'avoir ajouté à cette réponse. C'est peut-être l'erreur la plus courante à faire, c'est pourquoi vous obtenez mon vote favorable.
rdrey
La même chose se produit lors de la surcharge d'un @classmethod, la solution étant d'utiliser à la super().function(...)place de <parentclass>.function(cls, ...).
ederag
30

Cela peut être évident, mais cela pourrait aider quelqu'un qui ne l'a jamais vu auparavant. Cela se produit également pour les fonctions normales si vous attribuez par erreur un paramètre par position et explicitement par nom.

>>> 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
la source
6

ajoutez simplement le décorateur 'staticmethod' à la fonction et le problème est résolu

class foo(object):
    @staticmethod
    def foodo(thing=None, thong='not underwear'):
        print thing if thing else "nothing" 
        print 'a thong is',thong
adi gunawan
la source
Je viens de recevoir cette erreur et cette solution a résolu mon problème. Mais pouvez-vous expliquer comment ou pourquoi ce décorateur a résolu le problème?
Ray
1
staticmethod empêche la méthode de recevoir self comme premier argument. Alors maintenant, si vous appelez myfoo.foodo(thing="something"), chose = "quelque chose" sera assigné au premier argument, plutôt qu'à l'argument self implicite.
danio
cela signifie également que vous ne pouvez pas accéder aux variables de classe dans la fonction, qui sont généralement accessibles viaself
drevicko
4

Je veux ajouter une autre réponse:

Cela se produit lorsque vous essayez de transmettre un paramètre de position avec un ordre de position incorrect avec un argument de mot-clé dans la fonction d'appel.

there is difference between parameter and argumentvous pouvez lire en détail ici Arguments et paramètres en python

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


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

puisque nous avons trois paramètres:

a est un paramètre de position

b = 1 est le mot-clé et le paramètre par défaut

* args est un paramètre de longueur variable

donc nous affectons d'abord un comme paramètre de position, ce qui signifie que nous devons fournir une valeur à l'argument de position dans son ordre de position, ici, l'ordre est important. mais nous passons l'argument 1 à la place de a dans la fonction d'appel, puis nous fournissons également une valeur à a, en le traitant comme un argument mot-clé. maintenant a deux valeurs:

l'un est la valeur de position: a = 1

la seconde est la valeur mot-clé qui est a = 12

Solution

Nous devons changer hello(1, 2, 3, 4,a=12)pour hello(1, 2, 3, 4,12) que maintenant a n'obtienne qu'une seule valeur de position qui est 1 et b obtiendra la valeur 2 et le reste des valeurs obtiendra * args (paramètre de longueur variable)

Information additionnelle

si nous voulons que * args obtienne 2,3,4 et a devrait obtenir 1 et b devrait obtenir 12

alors on peut faire comme ça
def hello(a,*args,b=1): pass hello(1, 2, 3, 4,b=12)

Quelque chose de plus :

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

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

production :

1

(2, 1, 2, 8, 9)

1

{'c': 12}
Aaditya Ura
la source
c'est déjà couvert par la réponse stackoverflow.com/a/31822875/12663 - votre exemple a le même paramètre (a) attribué par position et aussi explicitement par nom.
danio
3

Cette erreur peut également se produire si vous passez un argument de mot clé pour lequel l'une des clés est similaire (a le même nom de chaîne) à un argument de position.

>>> 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'
>>> 

«feu» a été accepté dans l'argument «bar». Et pourtant, il y a un autre argument «bar» présent dans les kwargs.

Vous devrez supprimer l'argument mot-clé des kwargs avant de le transmettre à la méthode.

déverrouille-moi
la source
1

Cela peut également se produire dans Django si vous utilisez jquery ajax pour une URL qui retourne à une fonction qui ne contient pas le paramètre 'request'

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


def myfunc(foo, bar):
    ...
lukeaus
la source