关于func:python位置参数和关键字参数

python positional args and keyword args

我正在阅读mercurial的源代码,并在commands.py中找到了这样一个func def:

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

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


看看PEP 3102,它似乎也与此有关。

总而言之,补丁和opt可以接受变量参数,但后面的接受关键字参数。关键字参数作为字典传递,其中作为变量的位置参数将包装为元组。

从你的例子中

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

u1,repo and patch1之后的任何位置参数都将被包装为补丁中的元组。变量位置参数后面的任何关键字参数都将通过opt包装为字典对象。

另一件重要的事情是,呼叫方有责任确保不违反non-keyword arg after keyword arg的条件。

因此,违反这一点会导致语法错误。

例如

像呼叫

1
2
3
4
 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")

是有效的,但

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

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

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

1
2
u1=1,repo=2,patch1=3,patches=()and opts="test""测试<div class="suo-content">[collapse title=""]<ul><li>谢谢你的例子和参考,我想我得到了我的答案。python的参数非常灵活,在这个问题之前我对它的了解还不够。</li></ul>[/collapse]</div><hr><P>可能您有点混淆了函数定义和函数调用语法。</P><P><wyn>patch1</wyn>不是关键字arg,它是一个指定了默认参数值的位置参数。</P><P><wyn>*patches</wyn>是参数列表,而不是位置参数。</P><P>请从官方教程中查看此部分:</P><ul><li>http://docs.python.org/tutorial/controlflow.html关于定义函数的更多信息</li></ul><P>现在让我以这个函数为例总结一下要点:</P>[cc lang="python"]def f1(a1, a2, a3=None, *args, **kwargs):
  print a1, a2, a3, args, kwargs

函数定义

您有许多由名称(a1a2a3显式定义的参数,其中a3将默认由None初始化,如果在调用期间不提供。需要在该函数的任何有效调用中提供参数a1a2

可以使用附加参数调用函数,这些参数将出现在字典kwargs中(当关键字提供时)或列表args中(当关键字不提供时)。如果函数定义中不存在argskwargs,则除了函数定义中为函数调用显式命名的参数外,不允许调用方添加更多参数。

在函数定义中,需要先指定不带默认初始值设定项的显式参数,然后指定带默认初始值设定项的显式参数,再指定参数列表,最后指定关键字参数字典。

函数调用

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

1
2
3
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

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

调用函数时,需要首先放置非关键字参数,最后放置关键字参数,例如

1
2
3
4
# 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中。


我相信当调用函数时:

1
function(arg1="value")

这将使用"keyword"参数,但在定义函数的接口时:

1
def function(arg1="value"):

您正在定义"默认值"。()

因此,为了回答您的问题,在位置参数之后有一个默认值是完全正常的,在关键字之前调用带有非关键字参数的函数也是如此。

还要注意,调用函数时,关键字后不能有非关键字参数。


因为如果关键字参数的位置明确,则将其传递给它是可选的。观察:

1
2
3
4
5
6
7
8
9
10
>>> 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异常。

编辑:Moooeeep在他的回答中说

"patch1 is not a keyword arg, it's a positional arg with a default argument value assigned."

这并不是错的,但下面的IMO案例说明了为什么这样的定义是模棱两可的:

1
2
3
4
5
6
7
>>> 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

嗯!