metoda class generuje „TypeError:… otrzymano wiele wartości dla argumentu słowa kluczowego…”

131

Jeśli zdefiniuję metodę klasy za pomocą argumentu słowa kluczowego:

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

wywołanie metody generuje TypeError:

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

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

Co się dzieje?

drevicko
źródło
2
Nigdy nie uzyskasz satysfakcjonującej odpowiedzi, dlaczego wyraźne selfjest lepsze niż niejawne this.
nurettin

Odpowiedzi:

165

Problem polega na tym, że pierwszy argument przekazywany do metod klasowych w Pythonie jest zawsze kopią instancji klasy, na której metoda jest wywoływana, zwykle oznaczoną etykietą self. Jeśli klasa jest zadeklarowana w ten sposób:

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

zachowuje się zgodnie z oczekiwaniami.

Wyjaśnienie:

Bez selfpierwszego parametru, gdy myfoo.foodo(thing="something")jest wykonywana, foodometoda jest wywoływana z argumentami (myfoo, thing="something"). Instancja myfoojest następnie przypisywana do thing(ponieważ thingjest to pierwszy zadeklarowany parametr), ale Python również próbuje przypisać "something"do thing, stąd wyjątek.

Aby zademonstrować, spróbuj uruchomić to z oryginalnym kodem:

myfoo.foodo("something")
print
print myfoo

Będziesz drukować jak:

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

<__main__.foo object at 0x321c290>

Możesz zobaczyć, że „rzecz” została przypisana jako odniesienie do instancji „myfoo” klasy „foo”. Ta sekcja dokumentacji wyjaśnia nieco bardziej, jak argumenty funkcji działają.

drevicko
źródło
1
uwaga: możesz otrzymać ten sam typ błędu, jeśli twoja funkcja def zawiera self jako pierwszy parametr, a następnie przypadkowo wywołujesz funkcję również z self jako pierwszym parametrem.
Christopher Hunter,
48

Dzięki za pouczające posty. Chciałbym tylko zwrócić uwagę, że jeśli otrzymujesz komunikat „TypeError: foodo () ma wiele wartości argumentu słowa kluczowego„ rzecz ””, może być również tak, że omyłkowo przekazujesz „self” jako parametr, gdy wywołanie funkcji (prawdopodobnie dlatego, że skopiowałeś wiersz z deklaracji klasy - to częsty błąd, gdy się spieszysz).

joe_doe
źródło
7
To właśnie mi się przytrafiło, dziękuję za dodanie do tej odpowiedzi. To może być częstszy błąd, dlatego otrzymujesz mój głos za.
rdrey
To samo dzieje się podczas przeciążania an @classmethod, rozwiązaniem jest użycie super().function(...)zamiast <parentclass>.function(cls, ...).
ederag
30

To może być oczywiste, ale może pomóc komuś, kto nigdy wcześniej tego nie widział. Dzieje się tak również w przypadku zwykłych funkcji, jeśli omyłkowo przypiszesz parametr według pozycji i jawnie według nazwy.

>>> 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
źródło
6

wystarczy dodać dekorator „staticmethod” do funkcji i problem zostanie naprawiony

class foo(object):
    @staticmethod
    def foodo(thing=None, thong='not underwear'):
        print thing if thing else "nothing" 
        print 'a thong is',thong
adi gunawan
źródło
Właśnie dostałem ten błąd i to rozwiązanie rozwiązało mój problem. Ale czy możesz wyjaśnić, jak lub dlaczego ten dekorator rozwiązał problem?
Ray
1
staticmethod zatrzymuje metodę otrzymującą self jako pierwszy argument. Więc teraz, jeśli zadzwonisz myfoo.foodo(thing="something"), rzecz = "coś" zostanie przypisana do pierwszego argumentu, a nie do niejawnego argumentu własnego.
danio
oznacza to również, że nie możesz uzyskać dostępu do zmiennych klas w ramach funkcji, do których zwykle uzyskuje się dostęp przezself
drevicko
4

Chcę dodać jeszcze jedną odpowiedź:

Dzieje się tak, gdy próbujesz przekazać parametr pozycyjny z niewłaściwą kolejnością pozycji wraz z argumentem słowa kluczowego w wywołaniu funkcji.

there is difference between parameter and argumentmożesz przeczytać szczegółowo o Argumentach i parametrach w Pythonie

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


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

ponieważ mamy trzy parametry:

a jest parametrem pozycyjnym

b = 1 to słowo kluczowe i parametr domyślny

* args to parametr o zmiennej długości

więc najpierw przypisujemy jako parametr pozycyjny, co oznacza, że ​​musimy podać wartość argumentowi pozycyjnemu w jego kolejności pozycji, tutaj kolejność ma znaczenie. ale przekazujemy argument 1 w miejscu a w wywoływanej funkcji, a następnie przekazujemy również wartość a, traktując ją jako argument słowa kluczowego. teraz a mają dwie wartości:

jeden to wartość pozycyjna: a = 1

druga to wartość ze słowem kluczowym a = 12

Rozwiązanie

Musimy zmienić hello(1, 2, 3, 4,a=12)na, hello(1, 2, 3, 4,12) więc teraz a otrzyma tylko jedną wartość pozycyjną, która wynosi 1, a b otrzyma wartość 2, a reszta wartości otrzyma * args (parametr o zmiennej długości)

Dodatkowe informacje

jeśli chcemy, aby * argumenty otrzymały 2,3,4, a a powinno otrzymać 1, a b powinno otrzymać 12

wtedy możemy to zrobić
def hello(a,*args,b=1): pass hello(1, 2, 3, 4,b=12)

Coś więcej :

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

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

wynik :

1

(2, 1, 2, 8, 9)

1

{'c': 12}
Aaditya Ura
źródło
jest to już objęte odpowiedzią stackoverflow.com/a/31822875/12663 - Twój przykład ma ten sam parametr (a) przypisany przez pozycję, a także wyraźnie przez nazwę.
danio
3

Ten błąd może się również zdarzyć, jeśli przekażesz argument słowa kluczowego, dla którego jeden z kluczy jest podobny (ma tę samą nazwę ciągu) do argumentu pozycyjnego.

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

„ogień” został zaakceptowany w argumencie „bar”. A jednak w kwargach występuje inny argument „bar”.

Musiałbyś usunąć argument słowa kluczowego z kwargs przed przekazaniem go do metody.

odblokuj mnie
źródło
1

Może się to również zdarzyć w Django, jeśli używasz jquery ajax dla adresu URL, który odwraca się do funkcji, która nie zawiera parametru 'request'

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


def myfunc(foo, bar):
    ...
Lukeaus
źródło