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})
为什么前者呼叫工作但后者不工作?我的意思是不应该第一次返回错误?不*只是解压缩一个iterable?
最佳答案 正如
the docs所说:
If the syntax
*expression
appears in the function call,expression
must evaluate to an iterable. Elements from this iterable are treated as if they were additional positional arguments; if there are positional arguments x1, …, xN, and expression evaluates to a sequence y1, …, yM, this is equivalent to a call with M+N positional arguments x1, …, xN, y1, …, yM.A consequence of this is that although the
*expression
syntax may appear after some keyword arguments, it is processed before the keyword arguments…
许多人对功能定义具有类似的,有时是误导性相似的语法这一事实感到困惑.
在函数定义中,变量参数参数(如* args)位于任何仅关键字参数之前.当然,仅限关键字并且具有默认值是完全独立的,但是仅关键字参数都具有默认值是很常见的.所以,语法通常看起来像def func(a,* args,c = 4,** kwargs):.这可能会让你期望func(1,*(2,),c = 3,** {‘d’:4}成为匹配的调用语法,即使它不是.只记得def func(a = 1,* args,c,** kwargs)是完全合法的,它仍然是一个位置或关键字参数和ca关键字参数.
如果你对CPython中它的工作原理感兴趣(尽管其他实现可能都非常相似):
函数调用本身被编译为传递堆栈上表达式的值,仍然与普通参数分开.它位于解释器内部,在函数调用求值程序中,函数体执行的堆栈框架被构建,其中该值被分解为额外的参数.
查看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
被执行时它仍然在堆栈上.您可以在文档中查看操作码,其中说:
Calls a function.
argc
is interpreted as inCALL_FUNCTION
. The top element on the stack contains the keyword arguments dictionary, followed by the variable-arguments tuple, followed by explicit keyword and positional arguments.
因此,“变量参数元组”仍然是分开的.