Python:闭包

Posted KumataのBloG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python:闭包相关的知识,希望对你有一定的参考价值。

闭包(Closure)

       在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

命名空间与作用域

      我们可以把命名空间看做一个大型的字典类型(Dict),里面包含了所有变量的名字和值的映射关系。在 Python 中,作用域实际上可以看做是“在当前上下文的位置,获取命名空间变量的规则”。在 Python 代码执行的任意位置,都至少存在三层嵌套的作用域:

  • local            最内层作用域,最早搜索,包含所有局部变量**(Python 默认所有变量声明均为局部变量)**
  • non-local    所有包含当前上下文的外层函数的作用域,由内而外依次搜索,这里包含的是非局部也非全局的变量
  • global         一直向上搜索,直到当前模块的全局变量
  • built-in        最外层,最后搜索的,内置(built-in)变量

       在任意执行位置,可以将作用域看成是对下面这样一个命名空间的搜索:


scopes =
{ "local": {"locals": None, "non-local": {"locals": None, "global": {"locals": None, "built-in": ["built-ins"]}}}, }
 

       除了默认的局部变量声明方式,Python 还有globalnonlocal两种类型的声明(nonlocal是Python 3.x之后才有,2.7没有),其中 global 指定的变量直接指向(3)当前模块的全局变量,nonlocal则指向(2)最内层之外,global以内的变量。这里需要强调指向(references and assignments)的原因是,普通的局部变量对最内层局部作用域之外只有**只读(read-only)**的访问权限,比如下面的例子:

>>>x = 100
>>def main():
    x += 1
    print(x)

>>>main()
Traceback (most recent call last):
  File "<pyshell#50>", line 1, in <module>
    main()
  File "<pyshell#49>", line 2, in main
    x += 1
UnboundLocalError: local variable x referenced before assignment

这里抛出UnboundLocalError,是因为main()函数内部的作用域对于全局变量x仅有只读权限,想要在main()中对x进行改变,不会影响全局变量,而是会创建一个新的局部变量,显然无法对还未创建的局部变量直接使用x += 1。如果想要获得全局变量的完全引用,则需要global声明:

>>>x = 100
>>>def main():
    global x
    x += 1
    print(x)
    
>>>main()
print(x) # 全局变量已被改变
101

Python闭包练习

到这里基本上已经了解了 Python 作用域的规则,我们仿照 JavaScript 写一个计数器的闭包:

"""
/* JavaScript Closure example */
var inc = function(){  
  var x = 0;
  return function(){
    console.log(x++);
  };
};
var inc1 = inc()
var inc2 = inc()
"""
# Python 3.6
def inc():
    x = 0
    def inner():
        nonlocal x
        x += 1
        print(x)
    return inner
inc1 = inc()
inc2 = inc()

inc1()
inc1()
inc1()
inc2()
1
2
3
1

上面的例子中,inc1()是在全局环境下执行的,虽然全局环境是不能向下获取到inc()中的局部变量x的,但是我们返回了一个inc()内部的函数inner(),而inner()inc()中的局部变量是有访问权限的。也就是说inner()inc()内的局部作用域打包送给了inc1inc2,从而使它们各自独立拥有了一块封闭起来的作用域,不受全局变量或者任何其它运行环境的影响,因此称为闭包。

闭包函数都有一个__closure__属性,其中包含了它所引用的上层作用域中的变量:

print(inc1.__closure__[0].cell_contents)
print(inc2.__closure__[0].cell_contents)
[3]
[1]

 

 

原文出处: rainyear   

参考

  1. 9.2. Python Scopes and Namespaces
  2. Visualize Python Execution
  3. Wikipedia::Closure


以上是关于Python:闭包的主要内容,如果未能解决你的问题,请参考以下文章

Python进阶闭包(Closure)

python闲谈--闭包

python中对 函数 闭包 的理解

Python深入04 闭包

理解Python中的闭包

python闭包