Die Klassenmethode generiert "TypeError: ... hat mehrere Werte für das Schlüsselwortargument ..."

129

Wenn ich eine Klassenmethode mit einem Schlüsselwortargument so definiere:

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

Durch Aufrufen der Methode wird Folgendes generiert TypeError:

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

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

Was ist los?

drevicko
quelle
1
Sie werden nie eine zufriedenstellende Antwort erhalten, warum explizit selfbesser als implizit ist this.
Nurettin

Antworten:

163

Das Problem ist, dass das erste Argument, das in Python an Klassenmethoden übergeben wird, immer eine Kopie der Klasseninstanz ist, auf der die Methode aufgerufen wird, normalerweise mit der Bezeichnung self. Wenn die Klasse folgendermaßen deklariert ist:

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

es verhält sich wie erwartet.

Erläuterung:

Ohne selfals ersten Parameter myfoo.foodo(thing="something")wird die foodoMethode bei der Ausführung mit Argumenten aufgerufen (myfoo, thing="something"). Die Instanz myfoowird dann zugewiesen thing(da thingder erste deklarierten Parameter ist), aber auch Versuche Python zuzuweisen , "something"um thingsomit die Ausnahme.

Versuchen Sie dies zu demonstrieren mit dem Originalcode:

myfoo.foodo("something")
print
print myfoo

Sie werden wie folgt ausgeben:

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

<__main__.foo object at 0x321c290>

Sie können sehen, dass 'thing' ein Verweis auf die Instanz 'myfoo' der Klasse 'foo' zugewiesen wurde. In diesem Abschnitt der Dokumentation wird erläutert, wie Funktionsargumente etwas besser funktionieren.

drevicko
quelle
1
Hinweis: Sie können den gleichen Fehlertyp erhalten, wenn Ihre Funktion def self als ersten Parameter enthält und Sie die Funktion dann versehentlich auch mit self als erstem Parameter aufrufen.
Christopher Hunter
48

Danke für die lehrreichen Beiträge. Ich möchte nur darauf hinweisen, dass wenn Sie "TypeError: foodo () hat mehrere Werte für das Schlüsselwortargument 'thing'" erhalten, es auch sein kann, dass Sie fälschlicherweise das 'self' als Parameter übergeben, wenn Aufruf der Funktion (wahrscheinlich, weil Sie die Zeile aus der Klassendeklaration kopiert haben - dies ist ein häufiger Fehler, wenn Sie es eilig haben).

joe_doe
quelle
7
Das ist mir passiert, danke, dass du diese Antwort ergänzt hast. Dies könnte der häufigste Fehler sein, weshalb Sie meine Gegenstimme erhalten.
Rdrey
Das gleiche geschieht , wenn eine Überlastung @classmethod, die Lösung zu verwenden ist , super().function(...)statt <parentclass>.function(cls, ...).
ederag
30

Dies mag offensichtlich sein, aber es könnte jemandem helfen, der es noch nie zuvor gesehen hat. Dies gilt auch für reguläre Funktionen, wenn Sie einen Parameter fälschlicherweise nach Position und explizit nach Namen zuweisen.

>>> 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
quelle
6

Fügen Sie einfach den Dekorator 'staticmethod' zur Funktion hinzu, und das Problem ist behoben

class foo(object):
    @staticmethod
    def foodo(thing=None, thong='not underwear'):
        print thing if thing else "nothing" 
        print 'a thong is',thong
Adi Gunawan
quelle
Ich habe gerade diesen Fehler bekommen und diese Lösung hat mein Problem gelöst. Aber können Sie näher erläutern, wie oder warum dieser Dekorateur das Problem gelöst hat?
Ray
1
staticmethod stoppt die Methode, die self als erstes Argument empfängt. Wenn Sie jetzt aufrufen myfoo.foodo(thing="something"), wird thing = "etwas" eher dem ersten Argument als dem impliziten Selbstargument zugewiesen.
Danio
es bedeutet auch, dass Sie nicht auf Klassenvariablen innerhalb der Funktion zugreifen können, auf die normalerweise überself
drevicko
4

Ich möchte noch eine Antwort hinzufügen:

Dies passiert, wenn Sie versuchen, Positionsparameter mit falscher Positionsreihenfolge zusammen mit dem Schlüsselwortargument in der aufrufenden Funktion zu übergeben.

there is difference between parameter and argumentSie können hier ausführlich Argumente und Parameter in Python lesen

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


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

da wir drei Parameter haben:

a ist Positionsparameter

b = 1 ist Schlüsselwort und Standardparameter

* args ist ein Parameter variabler Länge

Also weisen wir zuerst einen als Positionsparameter zu, dh wir müssen dem Positionsargument in seiner Positionsreihenfolge einen Wert geben, hier ist die Reihenfolge von Bedeutung. Wir übergeben jedoch Argument 1 an der Stelle einer aufrufenden Funktion und geben dann auch einen Wert für a an, der als Schlüsselwortargument behandelt wird. Jetzt haben zwei Werte:

Eins ist der Positionswert: a = 1

Der zweite Wert ist der Schlüsselwortwert a = 12

Lösung

Wir müssen zu wechseln hello(1, 2, 3, 4,a=12), hello(1, 2, 3, 4,12) damit a jetzt nur einen Positionswert erhält, der 1 ist, und b den Wert 2 erhält und der Rest der Werte * args (Parameter variabler Länge) erhält.

zusätzliche Information

wenn wir wollen, dass * args 2,3,4 und a 1 und b 12 erhalten

dann können wir so machen
def hello(a,*args,b=1): pass hello(1, 2, 3, 4,b=12)

Etwas mehr :

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

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

Ausgabe :

1

(2, 1, 2, 8, 9)

1

{'c': 12}
Aaditya Ura
quelle
Dies wird bereits in der Antwort stackoverflow.com/a/31822875/12663 behandelt. In Ihrem Beispiel wird derselbe Parameter (a) nach Position und auch explizit nach Namen zugewiesen.
Danio
3

Dieser Fehler kann auch auftreten, wenn Sie ein Schlüsselwortargument übergeben, für das einer der Schlüssel einem Positionsargument ähnlich ist (denselben Zeichenfolgennamen hat).

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

"Feuer" wurde in das Argument "Bar" aufgenommen. Und doch gibt es in kwargs noch ein weiteres 'bar'-Argument.

Sie müssten das Schlüsselwortargument aus den kwargs entfernen, bevor Sie es an die Methode übergeben.

entsperren
quelle
1

Dies kann auch in Django passieren, wenn Sie jquery ajax verwenden, um eine URL zu erstellen, die zu einer Funktion umkehrt, die keinen 'request'-Parameter enthält

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


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