闭包的基本定义

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。详见 维基百科

Python 中的闭包

首先从例子引入 Python3 中的闭包

 #!/usr/bin/env python3
#-*- coding:utf-8 -*-
#File Name:01_closure.py
#Created Time:2019-01-09 09:32:20 def line(k, b):
# 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
# 这个内部函数的用到的相关变量即被称为闭包
# 本例中,k,b 和 x 即为闭包
def foo(x):
return k*x + b
return foo # 返回内部函数的引用 if __name__ == "__main__": print("***** y = x + 1 *****")
a = line(1,1)
print("x = 1 --> y = %d" % a(1))
print("x = 2 --> y = %d" % a(2))
print("x = 3 --> y = %d" % a(3)) print("***** y = 4 * x + 1 *****")
b = line(4,1)
print("x = 1 --> y = %d" % b(1))
print("x = 2 --> y = %d" % b(2))
print("x = 3 --> y = %d" % b(3))

例程

运行结果:

***** y = x + 1 *****
x = 1 --> y = 2
x = 2 --> y = 3
x = 3 --> y = 4
***** y = 4 * x + 1 *****
x = 1 --> y = 5
x = 2 --> y = 9
x = 3 --> y = 13

分析:

  1. 这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,通过line的参数 k, b 说明了这两个变量的取值,这样,就确定了函数的最终形式(y = x + 1和y = 4x + 1)。只需要变换参数a,b,就可以获得不同的直线表达函数。由此,可以看出,闭包具有跟函数类似的提高代码可复用性的作用。
  2. 如果没有闭包,每次新创建直线函数的时候需要同时指定 k,b,x。这样,就需要更多的参数传递,也减少了代码的可移植性。

注意点:

  1. 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,会消耗内存。
  2. 但是相较于类而言,其已经大大降低了内存占用

为了进一步的对 Python3 中的闭包的调用流程进行说明,再看以下例程:

 #!/usr/bin/env python3
#-*- coding:utf-8 -*-
#File Name: 02_closure_test.py
#Created Time:2019-01-09 09:32:20 import sys def line(k, b):
# 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
# 这个内部函数的用到的相关变量即被称为闭包
# 本例中,k,b 和 x 即为闭包
def foo(x):
return k*x + b
print("foo 函数的 id 为:\t\t%d" % id(foo))
return foo if __name__ == "__main__": print("***** y = x + 1 *****")
print("line 的 id 为: \t\t\t%d" % id(line))
a = line(1,1) # 会打印 foo 的 id
print("a = line(1,1),a 的 id 为: \t%d" % id(a))
print("x = 1 --> y = %d" % a(1))
print("***** y = 3*x + 2 *****")
print("line 的 id 为: \t\t\t%d" % id(line))
b = line(3,2) # 会打印 foo 的 id
print("b = line(3,2),b 的 id 为: \t%d" % id(b))
print("x = 1 --> y = %d" % b(1))

闭包例程

运行结果:

***** y = x + 1 *****
line 的 id 为: 2452122878432
foo 函数的 id 为: 2452124629872
a = line(1,1),a 的 id 为: 2452124629872
x = 1 --> y = 2
***** y = 3*x + 2 *****
line 的 id 为: 2452122878432
foo 函数的 id 为: 2452124630008
b = line(3,2),b 的 id 为: 2452124630008
x = 1 --> y = 5

从结果中可看出,line 函数的 id 没有发生改变,而不同闭包的 id 则各不相同。说明闭包在运行时可以有多个不同的实例

运行过程分析

Python 解释器运行到 1 处时,会将 line 函数加载到内存中。

运行到 2 处时,会调用内存中的 line 函数,创建(图中的 3 )相应的内存空间 8,通过 14 行中的 retrun 语句,使得 a 指向了内存 8 中的 foo 函数。 第 26 行执行的过程类似,只不过又开辟了一块新的内存空间。

由于 line 函数中的 foo 被外部程序所引用,因此 8,9 内存空间并不会随着 line 函数运行的结束而被释放。从而使用相应的 foo 函数可以被重复调用。

Python3 闭包修改外部变量的值

可以借助 noloacl 关键字,也可借助列表,下面的例程演示 nolocal 关键字实现的方法:

 #!/usr/bin/env python3
#-*- coding:utf-8 -*-
#File Name: 03_closure_modify_local_var.py
#Created Time:2019-01-09 09:32:20 import sys def line(k, b):
# 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
# 这个内部函数的用到的相关变量即被称为闭包
# 本例中,k,b 和 x 即为闭包
def foo(x):
nonlocal k
k += 1
print("***** k = %d" % k)
return k*x + b
return foo if __name__ == "__main__": print("***** y = x + 1 *****")
a = line(1,1)
print("x = 1 --> y = %d" % a(1))
print("x = 2 --> y = %d" % a(2))
print("x = 3 --> y = %d" % a(3)) print("***** y = 4*x + 1 *****")
b = line(4,1)
print("x = 1 --> y = %d" % b(1))
print("x = 2 --> y = %d" % b(2))
print("x = 3 --> y = %d" % b(3))

修改外部变量

运行结果:

***** y = x + 1 *****
***** k = 2
x = 1 --> y = 3
***** k = 3
x = 2 --> y = 7
***** k = 4
x = 3 --> y = 13
***** y = 4*x + 1 *****
***** k = 5
x = 1 --> y = 6
***** k = 6
x = 2 --> y = 13
***** k = 7
x = 3 --> y = 22

程序中如果没有用 nolocal 关键字,则程序会报错,因为 Python 解释器不能断定此时的 k 到底是函数 line 内的变量,还是函数 foo 类的局部变量。

Python学习笔记: 闭包的更多相关文章

  1. Python学习笔记(九)

    Python学习笔记(九): 装饰器(函数) 内置函数 1. 装饰器 1. 作用域 2. 高阶函数 3. 闭包 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就 ...

  2. Python学习笔记:装饰器

    Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...

  3. Python学习笔记之生成器、迭代器和装饰器

    这篇文章主要介绍 Python 中几个常用的高级特性,用好这几个特性可以让自己的代码更加 Pythonnic 哦 1.生成器 什么是生成器呢?简单来说,在 Python 中一边循环一边计算的机制称为 ...

  4. Python学习笔记(四)函数式编程

    高阶函数(Higher-order function) Input: 1 abs Output: 1 <function abs> Input: 1 abs(-10) Output: 1 ...

  5. python学习笔记整理——字典

    python学习笔记整理 数据结构--字典 无序的 {键:值} 对集合 用于查询的方法 len(d) Return the number of items in the dictionary d. 返 ...

  6. VS2013中Python学习笔记[Django Web的第一个网页]

    前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...

  7. python学习笔记之module && package

    个人总结: import module,module就是文件名,导入那个python文件 import package,package就是一个文件夹,导入的文件夹下有一个__init__.py的文件, ...

  8. python学习笔记(六)文件夹遍历,异常处理

    python学习笔记(六) 文件夹遍历 1.递归遍历 import os allfile = [] def dirList(path): filelist = os.listdir(path) for ...

  9. python学习笔记--Django入门四 管理站点--二

    接上一节  python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...

  10. python学习笔记--Django入门0 安装dangjo

    经过这几天的折腾,经历了Django的各种报错,翻译的内容虽然不错,但是与实际的版本有差别,会出现各种奇葩的错误.现在终于找到了解决方法:查看英文原版内容:http://djangobook.com/ ...

随机推荐

  1. ORA-06502 when awr report produce

    最近在生成一套系统的AWR报告时出现了如下报错:ORA-06502: PL/SQL: numeric or value error: character string buffer too small ...

  2. 048 Rotate Image 旋转图像

    给定一个 n × n 的二维矩阵表示一个图像.将图像旋转 90 度(顺时针).注意:你必须在原矩阵中旋转图像,请不要使用另一个矩阵来旋转图像.例 1:给出的输入矩阵 = [  [1,2,3],  [4 ...

  3. Redis 基础特性讲解

    目录 1.Redis基础杂项小节 1.是什么 2.能干嘛 3.去哪下 4.Redis启动后基础知识讲解 2.Redis数据类型 1.常用的五大数据类型 2.高级'玩家'才知道的其他数据类型 3.Red ...

  4. Ubuntu下安装Yarm-PM2

    首先打开yarm的官网.https://www.yarnpkg.com/zh-Hant/ (一)yarn的官方安装方法: 1.上通过 Debian 套件安裝 Yarn,粘贴以下命令 curl -sS ...

  5. Json 后台转对象的方式或者获取属性值方式。

    类似这类的 json字符串 后台转成 model 或者取其中一个属性值. 需要去掉前后引号 以及将转义字符去掉.空格代替 resoult = resoult.Substring(0, resoult. ...

  6. spring4、hibernate4整合xml配置

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  7. JavaScript判断图片是否已经加载完毕的方法汇总

    在网上有很多关于判断图片是否已经加载完毕的文章,但是有的浏览器并不适合,下面小编给大家分享一些有关JavaScript判断图片是否已经加载完毕方法汇总,具体内容如下所示: 一.onload事件 通过监 ...

  8. 模态框的理解 ,jQ: loading,进度条, 省级联动 表单验证 插件

    模态框: 打开一个弹框 不关闭它就不能做框外的操作 必须关闭或弹出另外的弹框 加载延迟loading + 进度条只要有请求 就处理一下监控ajax 全局事件jquery: $('#box').ajax ...

  9. 【css】css2实现两列三列布局的方法

    前言 对于 flex 弹性布局相信大家都有所了解,它是 css3 中的属性,然而它具有一定的兼容性问题.楼主前几天面试时遇到了面试官需要设计一个两列布局,我当然就说父元素 flex 吧哩吧啦,然而需要 ...

  10. mui对话框、表单

    1.mui.alert() 普通提醒参数 1.message Type: String 提示对话框上显示的内容 2.title Type: String 提示对话框上显示的标题 3.btnValue ...