Метод класса генерирует «TypeError:… получил несколько значений для ключевого слова аргумент…»

129

Если я определяю метод класса с аргументом ключевого слова следующим образом:

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

вызов метода генерирует TypeError:

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

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

В чем дело?

drevicko
источник
2
Вы никогда не получите удовлетворительного ответа на вопрос, почему явное selfлучше, чем неявное this.
nurettin

Ответы:

165

Проблема в том, что первый аргумент, передаваемый методам класса в python, всегда является копией экземпляра класса, для которого вызывается метод, обычно помеченного self. Если класс объявлен так:

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

он ведет себя так, как ожидалось.

Объяснение:

Без selfпервого параметра, когда myfoo.foodo(thing="something")выполняется, foodoметод вызывается с аргументами (myfoo, thing="something"). Затем экземпляр myfooназначается thing(поскольку thingэто первый объявленный параметр), но python также пытается назначить "something"его thing, следовательно, исключение.

Для демонстрации попробуйте запустить это с исходным кодом:

myfoo.foodo("something")
print
print myfoo

Вы выведете примерно так:

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

<__main__.foo object at 0x321c290>

Вы можете видеть, что 'thing' была назначена ссылка на экземпляр 'myfoo' класса 'foo'. В этом разделе документации немного подробнее объясняется, как работают аргументы функций.

drevicko
источник
1
примечание: вы можете получить тот же тип ошибки, если ваша функция def включает self в качестве первого параметра, а затем вы случайно вызываете функцию также с self в качестве первого параметра.
Кристофер Хантер,
48

Спасибо за поучительные посты. Я просто хотел бы отметить, что если вы получаете сообщение «TypeError: foodo () имеет несколько значений для аргумента ключевого слова 'thing'», возможно также, что вы ошибочно передаете 'self' в качестве параметра, когда вызов функции (вероятно, потому, что вы скопировали строку из объявления класса - это обычная ошибка, когда кто-то спешит).

joe_doe
источник
7
Вот что случилось со мной, спасибо за добавление к этому ответу. Это может быть наиболее распространенная ошибка, поэтому вы получаете мое одобрение.
rdrey
То же самое происходит при перегрузке @classmethod, решение - использовать super().function(...)вместо <parentclass>.function(cls, ...).
Эдераг
30

Это может быть очевидно, но может помочь тому, кто никогда этого раньше не видел. Это также происходит с обычными функциями, если вы ошибочно назначаете параметр по позиции и явно по имени.

>>> 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
источник
6

просто добавьте декоратор staticmethod к функции, и проблема будет исправлена

class foo(object):
    @staticmethod
    def foodo(thing=None, thong='not underwear'):
        print thing if thing else "nothing" 
        print 'a thong is',thong
Ади Гунаван
источник
Я только что получил эту ошибку, и это решение решило мою проблему. Но не могли бы вы подробнее рассказать, как и почему этот декоратор решил проблему?
Рэй
1
staticmethod останавливает прием метода self в качестве первого аргумента. Итак, теперь, если вы вызываете myfoo.foodo(thing="something"), thing = "something" будет назначен первому аргументу, а не неявному аргументу self.
danio
это также означает, что вы не можете получить доступ к переменным класса внутри функции, к которым обычно можно получить доступ черезself
drevicko
4

Хочу добавить еще один ответ:

Это происходит, когда вы пытаетесь передать позиционный параметр с неправильным порядком позиций вместе с аргументом ключевого слова в вызывающей функции.

there is difference between parameter and argumentвы можете подробно прочитать здесь Аргументы и Параметры в Python

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


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

поскольку у нас есть три параметра:

a - позиционный параметр

b = 1 - ключевое слово и параметр по умолчанию

* args - параметр переменной длины

поэтому мы сначала назначаем a как позиционный параметр, это означает, что мы должны предоставить значение позиционному аргументу в его порядке позиции, здесь порядок имеет значение. но мы передаем аргумент 1 вместо a в вызывающей функции, а затем мы также предоставляем значение a, рассматривая его как аргумент ключевого слова. теперь у a есть два значения:

один - позиционное значение: a = 1

второй - это ключевое слово, равное a = 12

Решение

Нам нужно изменить hello(1, 2, 3, 4,a=12)на, hello(1, 2, 3, 4,12) так что теперь a получит только одно позиционное значение, равное 1, а b получит значение 2, а остальные значения получат * args (параметр переменной длины)

Дополнительная информация

если мы хотим, чтобы * args получило 2,3,4, a - 1, а b - 12

тогда мы можем сделать вот так
def hello(a,*args,b=1): pass hello(1, 2, 3, 4,b=12)

Нечто большее :

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

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

вывод :

1

(2, 1, 2, 8, 9)

1

{'c': 12}
Аадитья Ура
источник
это уже описано в ответе stackoverflow.com/a/31822875/12663 - в вашем примере тот же параметр (a) назначен по позиции, а также явно по имени.
danio
3

Эта ошибка также может произойти, если вы передаете аргумент ключевого слова, для которого один из ключей похож (имеет такое же строковое имя) на позиционный аргумент.

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

«огонь» был принят в аргумент «бар». И все же у kwargs есть еще один аргумент о «решетке».

Вам нужно будет удалить аргумент ключевого слова из kwargs, прежде чем передавать его методу.

unlockme
источник
1

Также это может произойти в Django, если вы используете jquery ajax для URL-адреса, который обращается к функции, которая не содержит параметр запроса.

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


def myfunc(foo, bar):
    ...
lukeaus
источник