运行环境声明:本人的代码在sublime text 3中写的,可以Ctrl+b运行。python版本是python3.6。如果您直接运行的,请自觉加入if __name__ == '__main__'


认识闭包之前

在认识闭包之前,其实还应该知道,什么是嵌套函数(nested functions),什么是non-local变量(non-local variables)。

1.嵌套函数

嵌套函数:定义在其他函数内部的函数。嵌套函数可以访问enclosing scope的变量。enclosing scope是指一个封闭的作用域。在这里主要指定义嵌套函数的函数的作用域。即,嵌套函数可以访问外层变量。

2.non-local变量

要讲non-local变量,这里就会涉及python引用变量的顺序:

当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量(这里可以看到当前作用域的变量的执行优先级别最高)

在变量同名的情况下,执行优先级别高的的变量会把优先级别低的屏蔽掉。如:

s = 1 #全局变量
def outer_function():
s = 2 # 外层作用域变量
def iner_function():
s = 3 #当前作用域局部变量
print(s)
iner_function() outer_function()

输出结果为3

注意:我们是以iner_function函数为参考点展开讨论变量的作用域的。

上面输出可以看到,当前作用域局部变量将外层作用域变量和当前模块全局变量屏蔽掉了。

这里我假设有两个应用场景:

  1. 假如我要在当前作用域访问全局变量怎么办?(可以用gobal关键字)
  2. 假如我要在当前作用域访问外层作用域局部变量怎么办?(可以用nonlocal关键字)

global关键字:global关键字用来在函数或其他局部作用域中使用全局变量。但是如果不修改全局变量也可以不使用global关键字。

还是用刚才的例子:

s = 1 #全局变量
def outer_function():
def iner_function():
print(s)
iner_function() outer_function()

输出结果为1

因为不对s进行修改,且iner_function函数内部也没有局部变量。因此可以在不声明global的情况下访问到s。

但现在我要修改s的值:

s = 1 #全局变量
def outer_function():
def iner_function():
s += 1
print(s)
iner_function() outer_function()

出现错误:UnboundLocalError: local variable 's' referenced before assignment

此时应该用global声明s是全局变量:

s = 1 #全局变量
def outer_function():
def iner_function():
global s
s += 1
print(s)
iner_function() outer_function()
print(s)

输出结果为:

2
2
[Finished in 0.2s]

应该注意到,在修改s的值时,全局变量s的值也变了。也就是说,inner_function函数中的s和全局变量s是同一个变量。

nonlocal关键字:nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量。

s = 1
def outer_function():
s = 2 # 外层作用域变量
def iner_function():
nonlocal s
s += 1
print(s)
iner_function()
print("The value of s in outer_function:", s) outer_function()
print("The global value s:", s)

输出结果为:

3
The value of s in outer_function: 3
The global value s: 1
[Finished in 0.2s]

这里应该注意到nonlocal声明的变量s改变之后,并不对全局变量产生影响。

若要参考更多关于变量作用域访问问题,请看这里


什么是闭包?

好了,前面说了一大堆,现在开始看看,什么是闭包!

闭包(closure)是函数式编程的重要的语法结构,是词法闭包(Lexical Closure)的简称。是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境。

  1. 闭包是一个存储了函数及其环境的一个记录(record):一个将函数的自由变量和“值”(或者“变量名所绑定的引用”)关联起来的映射(mapping),自由变量是指在嵌套函数内使用但却是在定义嵌套函数的函数内定义的变量。
  2. 闭包不像普通函数,它允许通过“闭包复制到的值或者引用”来访问这些要被访问的对象的值,即使函数的调用发生在这些值的作用域外。
def outerFunction(text):
text = text def innerFunction():
print(text) return innerFunction # Note we are returning function WITHOUT parenthesis myFunction = outerFunction('Hey!')
myFunction()

输出是:hey!

通过观察,我们发现,闭包帮助我们在一个函数的作用域外调用这个函数。即,我们通过调用outerFunction函数来间接调用了inerFunction函数。要知道,我们是无法直接在inerFunction函数的作用域外调用它自己的。从另一个层面来讲,我们通过闭包间接扩大了inerFunction函数的作用域!

另外当执行完myFunction = outerFunction('Hey!')语句时,实际上是将一个闭包返回给myFunction。outerFunction('Hey!')执行完之后,实际上outerFunction函数里的变量text已经被销毁。但这里闭包已经将text记住了,所以我们执行myfunction()时,还是可以访问到text的内容!


什么时候或者为什么使用闭包?

将闭包作为一个返回函数,可以做到数据隐藏,从而减少对全局变量的使用。

当我们的代码只是要有很少的函数时,使用闭包是一个非常有效的方式。当有多个函数时,还是用类。


一个经典错误

flist = []

for i in xrange(3):
def func(x): return x * i
flist.append(func) for f in flist:
print f(2)

期待输出"0 2 4",但这个代码输出的是:"4 4 4"。

解释:flist.append(func)这句语句将每3个闭包(函数func)存储在列表flist中。这没错。但是,要注意到,在整个for循环for i in xrange(3):中,i的值并没有被销毁,而i的值是不断增加的,在这个循环中,每一个闭包对i的引用并没有“被切断”。到退出for循环时,i被销毁,同时,3个闭包(func函数)都对这个相同的i值进行了独立的存储。这里三个闭包是独立的没问题,但每一个闭包在最后存储i的时候,i的值已经是2了。

怎么避免这种错误呢?

看解决方法:

flist = []

for i in xrange(3):
def funcC(j):
def func(x): return x * j
return func
flist.append(funcC(i)) for f in flist:
print f(2)

这里可以轻易看到,当执行第一次循环时,i的值是0,return func执行之后,实际上funcC函数里的变量i就已经被销毁了,在销毁的那瞬间,第一个闭包就记住了第一个i的值。第二,三个闭包原理相同。可以参考here


到最后

参考,英文水平和表达能力有限,建议大家看文中我给出的连接。我是参考他们的文章写的。


版权:保留所有权,转载注明出处。


python中对闭包的理解的更多相关文章

  1. 说说Python中的闭包 - Closure

    转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...

  2. 说说Python中的闭包

    Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个 ...

  3. Python中的闭包 - Closure

    Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个 ...

  4. 21.python中的闭包和装饰器

    python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 以下说明主要针对 python ...

  5. python中对多态的理解

    目录 python中对多态的理解 一.多态 二.多态性 三.鸭子类型 python中对多态的理解 一.多态 多态是指一类事物有多种形态,比如动物类,可以有猫,狗,猪等等.(一个抽象类有多个子类,因而多 ...

  6. 【转】python中的闭包

    转自:http://www.cnblogs.com/ma6174/archive/2013/04/15/3022548.html python中的闭包 什么是闭包? 简单说,闭包就是根据不同的配置信息 ...

  7. 理解Python中的闭包

    1.定义 闭包是函数式编程的一个重要的语法结构,函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式).在面向过程编程中,我们见到过函数(function):在面向对象编程中,我们见 ...

  8. 轻松理解python中的闭包和装饰器 (下)

    在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...

  9. 轻松理解python中的闭包和装饰器(上)

    继面向对象编程之后函数式编程逐渐火起来了,在python中也同样支持函数式编程,我们平时使用的map, reduce, filter等都是函数式编程的例子.在函数式编程中,函数也作为一个变量存在,对应 ...

随机推荐

  1. Laravel Vuejs 实战:开发知乎 (2)用户注册

    1.本节需要发送验证邮件 2.教程使用SendCloud发送邮件 [我使用的是mailtrap] 3. composer require laravel/ui 安装完成后 php artisan ui ...

  2. tensorflow 学习记录

    函数变动 tf.train.SummaryWriter 变为 tf.summary.Filewritter 函数功能相同,仅仅是简单的重命名 ``` writer = tf.summary.FileW ...

  3. Java入门笔记 05-多线程

    介绍:Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程.本章主要内容为:多线程的创建.启动.控制以及同步操作,并介绍JDK 5新增的线程创建方式. 一.线程的创建与使用: 1 ...

  4. 世界协调时间(UTC)与中国标准时间

    整个地球分为二十四时区,每个时区都有自己的本地时间.在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC, Universal Time Coordinated).UTC与格 ...

  5. python opencv:使用滑动条做调色板

    cv2.getTrackbarPos() 函数的 一个参数是滑动条的名字, 第二个参数是滑动条被放置窗口的名字, 第三个参数是滑动条的默认位置. 第四个参数是滑动条的最大值, 第五个函数是回调函数,每 ...

  6. ubuntu 虚拟机添加多个站点

    我们安装好lamp环境,然后开始操作,比如一个站点叫test.ubuntu1.com,一个叫test.ubuntu2.com 1.修改hosts文件,路径/etc/hosts sudo vim /et ...

  7. python上传文件接口

    1.由于公司做接口测试,遇到了上传文件,一直搞了好久,原来是加了头部的原因def test_79(self): '''导入配置文件''' request = e['mysqlshujuku'] url ...

  8. MSE初始化和基本操作

    MSE默认的登录账户密码可能是:login/password (admin/admin). l 初始化配置完成后,下次使用root登录时,仅显示Linux shell提示符,而不是安装脚本. 您可以随 ...

  9. Java IO流详解(三)——字节流InputStream和OutPutStream

    我们都知道在计算机中,无论是文本.图片.音频还是视频,所有的文件都是以二进制(字节)形式存在的,IO流中针对字节的输入输出提供了一系列的流,统称为字节流.字节流是程序中最常用的流.在JDK中,提供了两 ...

  10. 修复GRUB引导故障!

    故障原因:MBR中的GRUB引导程序遭到破坏,grub.conf文件丢失,引导配置有误 故障现象:系统引导停滞,显示“grub>”提示符 解决思路:若无MBR备份,进入急救模式,重新安装grub ...