函数调用和变量

时间:2014-08-26 17:15:34

标签: python python-3.x

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})

为什么前者呼叫工作但后者不工作?我的意思是不应该第一次返回错误吗?不会解压缩一个可迭代的吗?

2 个答案:

答案 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元素包含关键字参数字典,后跟变量参数元组,后跟显式关键字和位置参数。

所以,"变量参数元组"仍然是分开的。