传递函数调用的参数的Python排序规则

传递函数调用的参数的Python排序规则,python,Python,我尝试使用*和**将任意数量的参数传递给函数。在Mark Lutz撰写的《学习Python》一书中,它说首先要遵循位置(值)的顺序,然后是关键字参数(name=value)和*序列的组合,然后是**dict。然而,我发现如果存在位置参数,则必须首先出现,但在一定程度上,其余三个参数可以按顺序混合 代码关键字3.py: def func(a, b=1, *pArgs, **kwArgs): print("a = {0}, b = {1}".format(a,b))

我尝试使用*和**将任意数量的参数传递给函数。在Mark Lutz撰写的《学习Python》一书中,它说首先要遵循位置(值)的顺序,然后是关键字参数(name=value)和*序列的组合,然后是**dict。然而,我发现如果存在位置参数,则必须首先出现,但在一定程度上,其余三个参数可以按顺序混合
代码关键字3.py:

def     func(a, b=1, *pArgs, **kwArgs): 
        print("a = {0}, b = {1}".format(a,b))
        print("Positional args = {}".format(pArgs))
        print("Keyword args = {}".format(kwArgs))
经过反复试验

[1] 在关键字和**dict之间,它们可以是任意顺序

>>> import keywords3 as j

>>> j.func(b = 3, **{'a':2,'c':4,'d':5})
a = 2, b = 3
Positional args = ()
Keyword args = {'d': 5, 'c': 4}
>>> j.func( **{'a':2}, b = 3, **{'c':4})
a = 2, b = 3
Positional args = ()
Keyword args = {'c': 4}
>>> j.func(*(2, 3), 4, *(5, 6))
a = 2, b = 3
Positional args = (4, 5, 6)
Keyword args = {}
>>> j.func(2, *(3, 4), 5, *(6,7), **{'c':8})
a = 2, b = 3
Positional args = (4, 5, 6, 7)
Keyword args = {'c': 8}
[2] 在位置参数和*序列之间,它们可以是任意顺序

>>> import keywords3 as j

>>> j.func(b = 3, **{'a':2,'c':4,'d':5})
a = 2, b = 3
Positional args = ()
Keyword args = {'d': 5, 'c': 4}
>>> j.func( **{'a':2}, b = 3, **{'c':4})
a = 2, b = 3
Positional args = ()
Keyword args = {'c': 4}
>>> j.func(*(2, 3), 4, *(5, 6))
a = 2, b = 3
Positional args = (4, 5, 6)
Keyword args = {}
>>> j.func(2, *(3, 4), 5, *(6,7), **{'c':8})
a = 2, b = 3
Positional args = (4, 5, 6, 7)
Keyword args = {'c': 8}
[3] 通常,位置或*序列参数需要出现在关键字或**dict参数之前

>>> j.func(*(3, 4), 5, *(6,7), d=15, **{'c':8}, e=16)
a = 3, b = 4
Positional args = (5, 6, 7)
Keyword args = {'e': 16, 'd': 15, 'c': 8}

>>> j.func(d=15, 5, *(6,7), **{'c':8}, e=16)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

>>> j.func(**{'a':2}, 5, *(6,7), **{'c':8}, e=16)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument unpacking

>>> j.func(**{'a':2}, *(6,7), **{'c':8}, e=16)
  File "<stdin>", line 1
SyntaxError: iterable argument unpacking follows keyword argument unpacking
这些观察结果正确吗?请发表评论。

有一条规则与所有示例一致:位置参数在命名参数之前

>>> j.func(*(3, 4), 5, *(6,7), d=15, **{'c':8}, e=16)
a = 3, b = 4
Positional args = (5, 6, 7)
Keyword args = {'e': 16, 'd': 15, 'c': 8}

>>> j.func(d=15, 5, *(6,7), **{'c':8}, e=16)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

>>> j.func(**{'a':2}, 5, *(6,7), **{'c':8}, e=16)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument unpacking

>>> j.func(**{'a':2}, *(6,7), **{'c':8}, e=16)
  File "<stdin>", line 1
SyntaxError: iterable argument unpacking follows keyword argument unpacking
在所有示例中,
*
***
都是解包运算符。比如说,当你写作的时候

f(1, *(2,3), 4)
语言越来越流行

f(1,2,3,4)
它们都是位置参数,语言不知道它们的区别。对于
**
操作符也是如此

然而,当你违反了唯一的规则,例如

j.func(**{'a':2}, 5, *(6,7), **{'c':8}, e=16)

您会得到一个错误,因为在本例中,
**{'a':2}
相当于
a=2
,它位于位置参数
5

之前。语法规则在以下位置可用:您混淆了函数调用时发生的列表/字典解包,以及定义函数时使用的参数列表/字典。两者使用相同的符号(*和**),但含义完全不同。例如,
f(*(1,2,3)
扩展为
f(1,2,3)
。1和2仍然是位置参数。同样地,
f(**{'c':8},e=16)
变成了
f(c=8,e=16)
,两个命名参数。不完全一样
f(a=1,*b)
在a
*
解包之前有一个关键字参数,但这是允许的。它的意思与更直观的
f(*b,a=1)
,以及
f(*b,a=1)
过去被禁止的意思相同。谢谢大家的精彩解释。@user2357112关于f(a=1,*b)被允许的说法是我在[4]异常中观察到的。与f(**a,*b)相比,f(**a,*b)是由于“单一规则”而不允许的,这种类型违反了单一规则。是吗?@LeonChang:语言必须知道哪个值对应哪个变量。在本例中,它起作用是因为您的命名参数恰好是第一个参数。尽管如此,我还是认为这会产生一个错误(我敢打赌,许多其他python程序员也是如此)。不管什么是有效的,可读性很重要,所以不要这样做。当函数真正接受项集合时,请使用
*
;当您确实有来自其他函数的类似dict/json的对象时,请使用
**
。避免同时使用这两种方法,在大多数情况下,两种方法都不要使用。