为什么如果存在相同名称的定位参数,kwargs中的key会被忽略?

2 投票
2 回答
1758 浏览
提问于 2025-04-18 07:08

我刚遇到了一种让我感到惊讶的情况:

def my_func(a=4, **kwargs):
    print kwargs

这是个演示:

>>> my_func(a=5, b=6)
{'b': 6}  # I was expecting {'a' : 4, 'b' : 6}
          # Maybe {'a' : 5, 'b' : 6}

另外,如果我看到:

SyntaxError: keyword argument repeated

像这样:

>>> my_func(a=4, a=5)
  File "<stdin>", line 1
SyntaxError: keyword argument repeated

或者看到TypeError: my_func() got multiple values for keyword argument 'a'

像这样:

>>> my_func(a=4, **{'a' : 5, 'b' : 6})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: my_func() got multiple values for keyword argument 'a'

那么Python是遵循什么规则来忽略关键字'a'的呢?

也许我漏掉了什么明显的东西,或者是一个重要的术语,但我通过搜索没有找到答案。

2 个回答

1

看起来,Python在处理函数参数时,会把任何以“arg=value”形式给出的参数都考虑在内,即使它们的顺序不对。这意味着这些“arg=value”对不会被放进**kwargs字典里(这个字典用来存放那些没有在参数列表中的名字/键)。

我们来看一个简单的例子:

#/usr/bin/python
def foo(a, **d):
    print 'a=', a
    print 'd=', d

foo(z=1)
## TypeError: foo() takes exactly 1 argument (0 given)
foo(a=1)
## a= 1
## d= {}
foo(z=1, a=2, b=3)
## a= 2
## d= {'z':1, 'b':3}
foo(a=1, z=2, 3)
## SyntaxError: non-keyword arg after keyword arg

注意,我在定义“foo”函数时用了一个位置参数,并且没有给它设置默认值;如果在参数列表中出现了“a=”,它会被识别出来,但如果没有提供这个参数,也不会报错,这时它的值会是“None”或者你设置的默认值。

有时候,区分参数和实参是很有用的。这个区分在常见的文档和网上讨论中很少被提及,所以可能有点学术化。不过,参数是函数定义中的一个名字,当你调用这个函数时,实参会绑定到这个参数上。可以把参数理解为占位符,而实参则是调用时的实际值。有时候,做出这样的区分可以让讨论,比如这个讨论,变得不那么混乱。

一个可能让人困惑的地方是关于def语句本身的讨论。在我的语句中:def foo(a=None, **d),a和d是参数,而a=None**d则是给这个定义语句的实参。这在中级Python程序员第一次遇到可变对象作为函数定义中的参数时,可能会特别困惑:def foo(mydict={}, mylist=[]): ... 当这个函数被调用时,有无参数会产生一些有趣的效果。理解这种函数的关键是,mydict和mylist绑定的是在函数定义时创建的对象。当函数在没有提供这些参数的情况下被调用时,这些对象是可见的,而当函数在提供了参数的情况下被调用时,这些参数会绑定到其他对象上。(如你所见,当把“实参”和“参数”这两个词混用时,这个概念几乎变得无法表达)。

5

这里说的code>a并不是被丢掉了,而是因为你在函数定义的时候明确地定义了它,所以它没有被包含在code>**kwargs里。如果我修改一下你原来的例子:

def my_func(a=4, **kwargs):
    print kwargs
    print a

然后测试一下:

>>> my_func(a=5, b=7)
{'b': 7}
5

code>**kwargs这个参数是用来收集在函数调用中传入的可选关键字参数,这些参数并没有在函数定义中明确列出。因为你在code>my_func的定义中包含了code>a=4,所以它不会被包含在code>**kwargs里。

这个内容在Python文档中有提到(我强调的部分):

当最后一个正式参数是code>**name的形式时,它会接收一个字典(见映射类型 — dict),这个字典包含了所有的关键字参数,除了那些对应于正式参数的参数。

撰写回答