想知道Python中闭包是什么吗?一起聊聊吧

135 篇文章 2 订阅
132 篇文章 1 订阅

前言

我觉得闭包更重要的是对作用域(Scope)的理解,因此把它单独列出来,同时可以理顺一下 Python 的作用域规则。

闭包的概念首先出现在函数式编程语言中,后来被一些命令式编程语言引用。尤其是在函数被用作头等公民的语言中,比如javascript(在javascript中,函数几乎可以被视为“头等公民”)。我写了一篇关于JavaScript闭包的文章(说明了JavaScript上下文和范围)。闭包实际上不是太复杂的概念,但是可以更好地理解闭包。同一语言的作用域规则。

命名空间与作用域

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

最内层作用域,最早搜索,包含所有局部变量(Python 默认所有变量声明均为局部变量)

所有包含当前上下文的外层函数的作用域,由内而外依次搜索,这里包含的是非局部也非全局的变量

一直向上搜索,直到当前模块的全局变量

最外层,最后搜索的,内置(built-in)变量

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

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

除了默认的局部变量声明方式,Python 还有global和nonlocal两种类型的声明(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()
UnboundLocalError                         Traceback (most recent call last)
 
 in ()
      3     x += 1
      4     print(x)
----> 5 main()
 
 in main()
      1 x = 100
      2 def main():
----> 3     x += 1
      4     print(x)
      5 main()
 
UnboundLocalError: local variable 'x' referenced before assignment`

小编推荐一个学python的学习qun 740,3222,34
无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

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

x = 100
def main():
    global x
    x += 1
    print(x)
 
main()
print(x) # 全局变量已被改变
101
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.5
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

对于还没有nonlocal关键字的 Python 2.7,可以通过一点小技巧来规避局部作用域只读的限制:

# Python 2.7
def inc():
    x = [0]
    def inner():
        x[0] += 1
        print(x[0])
    return inner
inc1 = inc()
inc2 = inc()
 
inc1()
inc1()
inc1()
inc2()
1
2
3
1

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

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

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

素材来源于网络 :侵删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值