def func(a, b, c, d): print(a, b, c, d)
func(1, c=3, *(2,), **{'d':4})
func(1, c=3, 2, **{'d':4})
为什么前者呼叫工作但后者不工作?我的意思是不应该第一次返回错误吗?不会解压缩一个可迭代的吗?
答案 0 :(得分:5)
位置参数必须始终出现在命名和解包参数之前。
在表达式中:
func(1, c=3, 2, **{'d':4})
2
是位置参数,而c=3
是命名参数。用这种方式写的是无效的。您必须在所有位置参数之后移动命名参数。
func(1, 2, c=3, **{'d':4})
另一方面,表达式:
func(1, c=3, *(2,), **{'d':4})
有效。 1
是此处唯一的位置参数。 c=3
是一个命名参数,*(2,)
和**{'d':4}
已解包。只要位置参数首先出现,它就全部有效。
答案 1 :(得分:2)
正如the docs所说:
如果语法
*expression
出现在函数调用中,expression
必须求值为iterable。来自此迭代的元素被视为它们是附加的位置参数;如果存在位置参数 x1,...,xN ,并且表达式求值为序列 y1,...,yM ,则这相当于使用M +调用N个位置参数 x1,...,xN,y1,...,yM 。这样做的结果是虽然
*expression
语法可能出现在某些关键字参数之后,但它会在关键字参数之前处理...
许多人对功能定义具有类似的,有时是误导性相似的语法这一事实感到困惑。
在函数定义中,变量参数参数(如*args
)位于任何仅关键字参数之前。当然,仅限关键字并且具有默认值是完全独立的,但是仅关键字参数都具有默认值是非常常见的。因此,语法通常看起来像def func(a, *args, c=4, **kwargs):
。这可以使您期望func(1, *(2,), c=3, **{'d': 4}
成为匹配的调用语法,即使它不是。请记住,def func(a=1, *args, c, **kwargs)
完全合法,它仍然使a
成为位置或关键字参数,c
成为仅限关键字参数。
如果您对CPython中的这种工作方式感兴趣(尽管其他实现可能都非常相似):
函数调用本身被编译为传递堆栈上的expression
的值,仍然与普通参数分开。它位于解释器内部,在函数调用求值程序中,函数体执行的堆栈框架被构建,其中该值被分解为额外的参数。
查看CPython如何解析和编译此代码可能会有所帮助:
>>> astpp(ast.parse("func(1, c=3, *(2,), **{'d':4})"))
Module(
body=[
Expr(
value=Call(
func=Name(id='func', ctx=Load()),
args=[Num(n=1)],
keywords=[keyword(arg='c', value=Num(n=3))],
starargs=Tuple(elts=[Num(n=2)], ctx=Load()),
kwargs=Dict(keys=[Str(s='d')], values=[Num(n=4)])))])"
即使您不了解AST,您也应该能够看到(2,)
在解析时仍然是独立的,存储在名为starargs
的字段中。
这会被编译为这个字节码:
2 0 LOAD_GLOBAL 0 (func)
3 LOAD_CONST 1 (1)
6 LOAD_CONST 2 ('c')
9 LOAD_CONST 3 (3)
12 LOAD_CONST 7 ((2,))
15 BUILD_MAP 1
18 LOAD_CONST 5 (4)
21 LOAD_CONST 6 ('d')
24 STORE_MAP
25 CALL_FUNCTION_VAR_KW 257
28 POP_TOP
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
你可能不太了解所有这些胡言乱语,但你可以看到元组(2,)
正在偏移量12加载到堆栈上,当操作码时它仍然在堆栈中CALL_FUNCTION_VAR_KW
被执行。您可以在文档中查看操作码,其中包含:
调用一个函数。
argc
被解释为CALL_FUNCTION
。堆栈中的top元素包含关键字参数字典,后跟变量参数元组,后跟显式关键字和位置参数。
所以,"变量参数元组"仍然是分开的。