我试图了解闭包在 Python 中的工作原理,我发现了以下代码片段:
def closure(): count = 0 def inner(): nonlocal count count += 1 print(count) return inner start = closure() start() # prints 1 start() # prints 2 start() # prints 3
我可以理解这段代码,因为当inner定义函数时,封闭范围有一个名为 count 的变量,其值为 0,然后内部函数将remember这个值
inner
remember
但是,如果我移动到count = 0内部函数下面,那么代码就变成这样:
count = 0
def closure(): def inner(): nonlocal count count += 1 print(count) count = 0 return inner start = closure() start() # prints 1 start() # prints 2 start() # prints 3
令我惊讶的是,代码仍然运行良好,这真的让我很困惑。因为在inner定义时,变量count并不存在于封闭范围内,inner函数如何能够记住此时命名空间中尚不存在的值?
count
是不是因为类似JS的变量提升的东西在Python中也存在?
这是一个很好的问题!在 Python 中,闭包的工作原理可能会让人感到困惑,尤其是当涉及到变量定义顺序时。让我们逐步分析这个问题。
首先,回顾一下什么是 闭包(Closure)。在 Python 中,闭包是指一个函数能够“记住”并访问它在定义时的环境(即变量)。它的关键是内部函数(如 inner)能够访问外部函数(如 closure)的变量,即使外部函数已经结束执行。
closure
在你的第一个代码片段中:
nonlocal count
接下来,你将 count = 0 放到 inner 函数之后:
def closure(): def inner(): nonlocal count count += 1 print(count) count = 0 return inner
你认为这会出错,因为在 inner 函数定义时,count 还没有被初始化。然而,Python 运行时并没有报错,代码依然能够按预期工作。为什么会这样呢?
这是因为 Python 中 作用域的解析规则 与 JavaScript 中的 “提升”(hoisting)有些不同,但也有相似之处。
0
在你的代码中,虽然 count 的赋值发生在 inner 函数定义之后,但实际上 Python 确保了 count 作为 inner 闭包的外部变量存在,而这个变量的初始值是在 closure 被调用时设置的。
因此,count 变量实际上是随着 closure 函数的执行被创建的,并且它在 inner 被调用时已经存在,并被 nonlocal 关键字修改。
nonlocal
在 JavaScript 中,变量声明(使用 var)会被“提升”,即使它们出现在函数体的后面,也会被视为在函数的顶部定义。而在 Python 中,并没有这种“提升”的机制。
var
在你的例子中,虽然 count 在 inner 函数定义之前没有赋值,但 Python 保证 count 会作为外部环境的一部分,允许 inner 引用它,这并不是因为“提升”机制,而是因为闭包的环境已在函数定义时就被绑定。
这个行为在 Python 中并不是由于变量提升,而是因为闭包捕获外部函数的环境和作用域,因此你可以在 inner 中引用 count,即使它的赋值出现在 inner 定义之后。