Python 位置参数与关键字参数

10 投票
4 回答
7623 浏览
提问于 2025-04-17 08:16

我在看mercurial的源代码时,发现了commands.py里有这样一个函数定义:

def import_(ui, repo, patch1=None, *patches, **opts):
    ...

在Python中,位置参数必须放在关键字参数前面。但是这里,patch1是一个关键字参数,后面跟着一个位置参数*patches。为什么这样写是可以的呢?

4 个回答

2

因为在传递关键字参数时,如果位置很明确,传递这个关键字是可以选择的。来看这个例子:

>>> def f(ui, patch1=None, *patches, **opts):
...     print patch1
... 
>>> f(1, 2)
2
>>> f(1, patch1='a', 3)
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
>>> f(1, 'a', 3)
a

正如你所看到的,省略patch1的关键字会让这个参数变成一个普通的位置参数,因此不会引发SyntaxError错误。


编辑:在他的回答中,moooeeeep提到:

“patch1 不是一个关键字参数,它是一个带有默认值的位置参数。”

这并没有错,但我认为下面的例子能更好地说明为什么这样的定义会让人困惑:

>>> def f(ui, p1=None, p2=None, *patches, **opts):
...    print p1, p2
... 
>>> f(1, 'a', 'b', 3)  #p2 is a positional argument with default value?
a b
>>> f(1, p2='b')  #p2 is a keyword argument?
None b

希望这对你有帮助!

3

可能你对函数的定义和调用方式有点混淆。

patch1 不是一个关键字参数,它是一个位置参数,并且有一个默认值。

*patches 是一个参数列表,而不是位置参数。


请看看官方教程中的这一部分:

现在让我用这个函数作为例子总结一下主要要点:

def f1(a1, a2, a3=None, *args, **kwargs):
  print a1, a2, a3, args, kwargs

函数定义

你有几个参数是明确用名字定义的(a1a2a3),其中如果在调用时没有提供 a3,它会默认被初始化为 None。而 a1a2 在调用这个函数时必须提供。

这个函数可以用额外的参数调用,这些额外参数会出现在字典 kwargs 中(如果是通过关键字提供的话),或者出现在列表 args 中(如果不是通过关键字提供的话)。如果函数定义中没有 argskwargs,那么调用者就不能添加除了函数定义中明确命名的参数以外的其他参数。

在函数定义中,你需要先指定没有默认值的参数,然后是有默认值的参数,接着是参数列表,最后是关键字参数字典。

函数调用

调用函数的方式有很多种。例如,以下调用会产生相同的结果:

f1(1, 2)       # pass a1 and a2 as positional arguments
f1(a2=2, a1=1) # pass a1 and a2 as keyword arguments
f1(1, a2=2)    # pass a1 as positional argument, a2 as keyword argument

也就是说,函数的参数可以通过它们的位置(位置参数,或者非关键字参数)来解析,或者通过它们指定的名字(关键字参数)来解析。

在调用函数时,你需要先放非关键字参数,最后放关键字参数,例如:

# demonstrate how some additional positional and keyword arguments are passed
f1(1, 2, 3, 4, 5, 6, 7, a4=8, a5=9, a6=10)
# prints:
# 1 2 3 (4, 5, 6, 7) {'a5': 9, 'a4': 8, 'a6': 10}

现在,不符合函数定义中指定参数列表的位置参数会被添加到参数列表 *args 中,而不符合函数定义中指定参数列表的关键字参数会被插入到关键字参数字典 **kwargs 中。

9

你可以看看这个 PEP 3102,它似乎和 这个问题有点关系。

简单来说,patches 和 opts 是用来接收可变参数的,但 opts 是用来接收关键字参数的。关键字参数是以字典的形式传递的,而 可变位置参数则会被包装成元组

根据你的例子

def import_(ui, repo, patch1=None, *patches, **opts):

u1, repo 和 patch1 之后的任何位置参数都会被包装成元组放在 patches 中。任何跟在可变位置参数后面的关键字参数都会通过 opts 被包装成字典对象。

还有一个重要的事情是,调用者需要确保不违反 关键字参数后面不能有非关键字参数 的规则。

如果违反这个规则,就会引发语法错误。

比如说

像这样的调用

 import_(1,2,3,test="test")
 import_(1,2,3,4,test="test")
 import_(1,2,3,4,5)
 import_(1,2,patch1=3,test="test")

是有效的,但

import_(1,2,3,patch1=4,5)

会引发语法错误 SyntaxError: non-keyword arg after keyword arg

在第一个有效的例子中 import_(1,2,3,test="test")

u1 = 1, repo = 2, patch1 = 3, patches = () and opts={"test":"test"}

在第二个有效的例子中 import_(1,2,3,patch1=4,test="test")

u1 = 1, repo = 2, patch1 = 3 , patches = (4) and opts={"test":"test"}

在第三个有效的例子中 import_(1,2,3,4,5)

u1 = 1, repo = 2, patch1 = 3 , patches=(4,5), and opts={}

在第四个有效的例子中 import_(1,2,patch1=3,test="test")

u1 = 1, repo = 2, patch1 = 3 , patches=(), and opts={"test":"test"}
you can use patch1 as a keywords argument but doing so you cannot wrap any variable positional arguments within patches

撰写回答