O método de classe gera "TypeError: ... obteve vários valores para o argumento da palavra-chave ..."

129

Se eu definir um método de classe com um argumento de palavra-chave assim:

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

chamar o método gera um TypeError:

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

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

O que está acontecendo?

drevicko
fonte
1
Você nunca receberá uma resposta satisfatória sobre por que o explícito selfé melhor do que implícito this.
Nurettin

Respostas:

163

O problema é que o primeiro argumento passado aos métodos de classe em python é sempre uma cópia da instância de classe na qual o método é chamado, normalmente rotulado self. Se a classe for declarada assim:

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

comporta-se como esperado.

Explicação:

Sem selfcomo o primeiro parâmetro, quando myfoo.foodo(thing="something")é executado, o foodométodo é chamado com argumentos (myfoo, thing="something"). A instância myfooé então atribuída a thing(já que thingé o primeiro parâmetro declarado), mas o python também tenta atribuir "something"a thing, daí a Exception.

Para demonstrar, tente executar isso com o código original:

myfoo.foodo("something")
print
print myfoo

Você produzirá como:

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

<__main__.foo object at 0x321c290>

Você pode ver que 'coisa' recebeu uma referência à instância 'myfoo' da classe 'foo'. Esta seção dos documentos explica como os argumentos das funções funcionam um pouco mais.

drevicko
fonte
1
Nota: você pode obter o mesmo tipo de erro se a função def incluir self como o primeiro parâmetro e, acidentalmente, chamar a função também com self como o primeiro parâmetro.
Christopher Hunter
48

Obrigado pelas mensagens instrutivas. Gostaria de manter uma observação de que, se você está recebendo "TypeError: foodo (), possui vários valores para o argumento de palavra-chave 'coisa'", também pode ser que você esteja passando o 'self' como parâmetro quando chamando a função (provavelmente porque você copiou a linha da declaração de classe - é um erro comum quando alguém está com pressa).

joe_doe
fonte
7
Foi o que aconteceu comigo, obrigado por adicionar a esta resposta. Esse pode ser o erro mais comum de cometer, e é por isso que você está recebendo meu voto positivo.
Rdrey
O mesmo ocorre ao sobrecarregar um @classmethod, sendo a solução usada em super().function(...)vez de <parentclass>.function(cls, ...).
Ederag
30

Isso pode ser óbvio, mas pode ajudar alguém que nunca viu isso antes. Isso também acontece para funções regulares se você atribuir um parâmetro por posição e explicitamente por nome.

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

basta adicionar o decorador 'staticmethod' para funcionar e o problema é corrigido

class foo(object):
    @staticmethod
    def foodo(thing=None, thong='not underwear'):
        print thing if thing else "nothing" 
        print 'a thong is',thong
adi gunawan
fonte
Acabei de receber este erro e esta solução resolveu meu problema. Mas você pode explicar como ou por que esse decorador resolveu o problema?
Ray
1
staticmethod interrompe o método de receber self como primeiro argumento. Portanto, agora, se você chamar myfoo.foodo(thing="something"), thing = "something" será atribuído ao primeiro argumento, em vez do auto-argumento implícito.
Danio 11/11
isso também significa que você pode variáveis de classe não acesso dentro da função, que geralmente são acessados por meioself
drevicko
4

Quero adicionar mais uma resposta:

Isso acontece quando você tenta passar o parâmetro posicional com a ordem de posição incorreta e o argumento da palavra-chave na função de chamada.

there is difference between parameter and argumentvocê pode ler em detalhes sobre aqui Argumentos e Parâmetros em python

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


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

já que temos três parâmetros:

a é parâmetro posicional

b = 1 é a palavra-chave e o parâmetro padrão

* args é um parâmetro de comprimento variável

então, primeiro atribuímos um parâmetro como posicional, significa que precisamos fornecer valor ao argumento posicional em sua ordem de posição, aqui a ordem importa. mas estamos passando o argumento 1 no lugar de uma função de chamada e, em seguida, também estamos fornecendo valor a, tratando como argumento de palavra-chave. agora a tem dois valores:

um é o valor posicional: a = 1

segundo é o valor com palavras-chave que é = 12

Solução

Temos que mudar hello(1, 2, 3, 4,a=12)parahello(1, 2, 3, 4,12) agora, a obterá apenas um valor posicional que é 1 e b obterá o valor 2 e o restante dos valores obterá * args (parâmetro de comprimento variável)

informação adicional

se quisermos que * args deve ter 2,3,4 e a deve ter 1 eb deve ter 12

então podemos fazer assim
def hello(a,*args,b=1): pass hello(1, 2, 3, 4,b=12)

Algo mais :

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

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

resultado :

1

(2, 1, 2, 8, 9)

1

{'c': 12}
Aaditya Ura
fonte
isso já é coberto pela resposta stackoverflow.com/a/31822875/12663 - seu exemplo tem o mesmo parâmetro (a) atribuído pela posição e também explicitamente pelo nome.
Danio 11/11
3

Este erro também pode ocorrer se você passar um argumento de palavra-chave para o qual uma das chaves é semelhante (tem o mesmo nome de sequência) a um 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'
>>> 

"fogo" foi aceito no argumento 'bar'. E, no entanto, há outro argumento 'bar' presente em kwargs.

Você precisaria remover o argumento de palavra-chave dos kwargs antes de passá-lo para o método

unlockme
fonte
1

Isso também pode acontecer no Django se você estiver usando jquery ajax para url que reverte para uma função que não contém o parâmetro 'request'

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


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