python – 函数调用和varagrs

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 in CALL_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.

因此,“变量参数元组”仍然是分开的.

点赞