Python的闭包如何工作

时间:2019-09-04 05:08:10

标签: python scope namespaces closures

我试图了解闭包在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将此值

但是,如果我然后将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函数如何能够记住此名称空间中不存在的值点了吗?

是因为Python中还存在类似于JS变量hoisting的东西吗?

2 个答案:

答案 0 :(得分:2)

在两个示例中,您都将在 start 变量中接收 closure 方法返回的值,即方法 closure 被执行并返回内部本地方法。因此,也可以定义 count 变量并将其初始化为值0

当您使用 start()调用 inner 方法时,该方法将被执行,并且到那时,在两种情况下,count变量都已经存在

但是,如果您有类似这样的代码,则会出现参考错误

def closure():
    def inner():
        nonlocal count
        count += 1
        print(count)
    inner()
    count = 0

start = closure()

在定义 count 之前,先调用内部方法

答案 1 :(得分:2)

来自Resolution of Names

  

如果在代码块内的任何地方进行了名称绑定操作,则该块内对该名称的所有使用都将视为对当前块的引用。绑定之前的一个块。这条规则很微妙。 Python缺少声明,并且允许名称绑定操作发生在代码块内的任何位置。 可以通过扫描代码块的整个文本以进行名称绑定操作来确定代码块的局部变量。

(强调我的。)

赋值为name binding operations,因此只要count = 0在函数中的任意位置存在,count就被视为该函数的局部变量,而{{ 1}}将引用该变量。 (尽管如果您在给inner赋值之前调用 inner(),则会出现异常。)