类方法生成“ 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'

这是怎么回事?


1
关于为什么显式self优于隐式的问题,您将永远无法获得满意的答案this
nurettin

Answers:


163

问题在于,传递给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使用arguments调用该方法(myfoo, thing="something")myfoo然后将该实例分配给thing(因为thing是第一个声明的参数),但是python也会尝试分配"something"thing,因此是Exception。

为了演示,请尝试使用原始代码运行它:

myfoo.foodo("something")
print
print myfoo

您将输出如下:

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

<__main__.foo object at 0x321c290>

您可以看到“事物”已被分配对类“ foo”的实例“ myfoo”的引用。文档的此部分说明了函数参数的工作原理。


1
注意:如果您的函数def包含self作为第一个参数,则可能会得到相同类型的错误,然后您意外地也将self作为第一个参数调用该函数。
克里斯托弗·亨特

48

感谢您的指导性帖子。我只想说明一下,如果您收到“ TypeError:foodo()为关键字参数'thing'获得多个值”,则可能是您错误地将“ self”作为参数传递的调用该函数(可能是因为您从类声明中复制了该行-急时这是一个常见错误)。


7
这就是我所经历的事情,感谢您添加此答案。这可能是更常见的错误,这就是为什么您要得到我的支持。
rdrey 2015年

过载的时相同的发生@classmethod,该溶液是使用super().function(...)代替<parentclass>.function(cls, ...)
ederag '16

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'

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

这也意味着您无法访问函数中的类变量,这些变量通常通过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传递给in调用函数中的位置,然后还将值提供给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 '16

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中删除,然后再将其传递给方法。


1

如果您使用jquery ajax的URL反向到不包含'request'参数的函数,则这也可能在Django中发生

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


def myfunc(foo, bar):
    ...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.