函数参数语义(嵌套函数)

Function parameter semantics (with nested functions)

我正在学习 python,但我正在努力了解 Python 如何处理闭包。我通常理解这是一个自由变量,它存储在内存中,在被调用者作用域过期后很长时间内可以访问。我很困惑这种设置是如何解决的。

def do_something(x):
    return lambda y: y + x;
f = do_something(4);
f(4)

这里的 f(4) 似乎使用了 lambda 函数签名而不是 do_something 签名。 它 returns 8,但内部是什么导致 python 使用 y 而不是 x 作为输入参数?我是否应该将 do_something 视为类似于 class 的定义,其中 f = do_something(4) 是构造函数而 f(4) 是要采取的实际操作?

请参阅 this 了解我第一次遇到此问题的地方。

表达式lambda y: y + x创建了一个新函数,这个函数由do_something返回给它的调用者。在这个函数中,x 是一个自由变量,闭包将参数的引用保存到用于创建它的 do_something 函数中。所以当你打电话给

f = do_something(4)

相当于:

f = lambda y: y + 4

相当于:

def f(y):
    return y + 4 

然后当您调用 f(4) 时,它会调用该函数,将 y 绑定到 f 参数。

你是对的,这类似于 class 构造函数的工作方式;在某些编程语言中,闭包被用作 class 个实例的内部实现。 类 添加诸如继承之类的内容,并且通常还提供更简单的语法来声明 class 属性和方法。

函数对象有一个属性,可以将值存储在 "cell" 中。您可以从 __closure__ 属性访问封闭变量:

>>> def do_something(x):
...     return lambda y: y + x;
... 
>>> f = do_something(4)
>>> f.__closure__[0]
<cell at 0x105d86600: int object at 0x7f905ac12bd0>

这似乎没什么用。幸运的是,获取单元格的内容也很容易:

>>> f.__closure__[0].cell_contents
4

好的,这只是谜题的第一部分(python 如何存储闭包数据)。让我们看看 f 的反汇编字节码,看看 python 对这些数据做了什么:

>>> import dis
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (y)
              3 LOAD_DEREF               0 (x)
              6 BINARY_ADD          
              7 RETURN_VALUE     

第一个代码 LOAD_FAST 用于加载局部变量(在 C 中存储为指向对象的指针数组 -- 0 表示这是第一个对象大批)。在这种情况下 y 是一个局部变量,因为它在函数的签名中。下一个代码是 [LOAD_DEREF](enter link description here),它实际上在单元格中查找并从闭包中加载值。这在低层次上完成了这个问题的图片......

在更高的层次上,你不需要担心这些。您只需要知道 python 函数能够查找包含它的任何范围的值。