闭包的基本定义

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

Python 中的闭包

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

 1 #!/usr/bin/env python3
 2 #-*- coding:utf-8 -*-
 3 #File Name:01_closure.py
 4 #Created Time:2019-01-09 09:32:20
 5
 6
 7 def line(k, b):
 8     # 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
 9     # 这个内部函数的用到的相关变量即被称为闭包
10     # 本例中,k,b 和 x 即为闭包
11     def foo(x):
12         return k*x + b
13     return foo  # 返回内部函数的引用
14
15
16 if __name__ == "__main__":
17
18     print("***** y = x + 1 *****")
19     a = line(1,1)
20     print("x = 1 --> y = %d" % a(1))
21     print("x = 2 --> y = %d" % a(2))
22     print("x = 3 --> y = %d" % a(3))
23
24     print("***** y = 4 * x + 1 *****")
25     b = line(4,1)
26     print("x = 1 --> y = %d" % b(1))
27     print("x = 2 --> y = %d" % b(2))
28     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 中的闭包的调用流程进行说明,再看以下例程:

 1 #!/usr/bin/env python3
 2 #-*- coding:utf-8 -*-
 3 #File Name: 02_closure_test.py
 4 #Created Time:2019-01-09 09:32:20
 5
 6 import sys
 7
 8 def line(k, b):
 9     # 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
10     # 这个内部函数的用到的相关变量即被称为闭包
11     # 本例中,k,b 和 x 即为闭包
12     def foo(x):
13         return k*x + b
14     print("foo 函数的 id 为:\t\t%d" % id(foo))
15     return foo
16
17
18 if __name__ == "__main__":
19
20     print("***** y = x + 1 *****")
21     print("line 的 id 为: \t\t\t%d" % id(line))
22     a = line(1,1) # 会打印 foo 的 id
23     print("a = line(1,1),a 的 id 为: \t%d" % id(a))
24     print("x = 1 --> y = %d" % a(1))
25     print("***** y = 3*x + 2 *****")
26     print("line 的 id 为: \t\t\t%d" % id(line))
27     b = line(3,2) # 会打印 foo 的 id
28     print("b = line(3,2),b 的 id 为: \t%d" % id(b))
29     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 关键字实现的方法:

 1 #!/usr/bin/env python3
 2 #-*- coding:utf-8 -*-
 3 #File Name: 03_closure_modify_local_var.py
 4 #Created Time:2019-01-09 09:32:20
 5
 6 import sys
 7
 8 def line(k, b):
 9     # 在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,
10     # 这个内部函数的用到的相关变量即被称为闭包
11     # 本例中,k,b 和 x 即为闭包
12     def foo(x):
13         nonlocal k
14         k += 1
15         print("***** k = %d" % k)
16         return k*x + b
17     return foo
18
19
20 if __name__ == "__main__":
21
22     print("***** y = x + 1 *****")
23     a = line(1,1)
24     print("x = 1 --> y = %d" % a(1))
25     print("x = 2 --> y = %d" % a(2))
26     print("x = 3 --> y = %d" % a(3))
27
28     print("***** y = 4*x + 1 *****")
29     b = line(4,1)
30     print("x = 1 --> y = %d" % b(1))
31     print("x = 2 --> y = %d" % b(2))
32     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 类的局部变量。

转载于:https://www.cnblogs.com/lijunjie9502/p/10245547.html

Python学习笔记: 闭包相关推荐

  1. python 学习笔记 (核心)

    python    学习笔记 (核心) Python解释器从头到尾一行接一行执行脚本 # -*- coding: UTF-8 -*-    //字符编码 不区分单引号和双引号,x='hello',x[ ...

  2. 我的python学习笔记全集_Python学习笔记(1):助你我一起共同成长

    最近在学习python时,萌生了一个想法,把自己在学习过程中的知识做一个笔记再分享出来,既帮助了大家又能让自己重温知识,今天就发一下python学习笔记(1),助你我一起成长! 一.Python初识与 ...

  3. 零基础学Python学习笔记

    Python学习笔记 代码下载地址 链接:https://pan.baidu.com/s/1yGnpfq4ZHeKpt4V0J_PTSg 提取码:hmzs 1. Python 基础语法 1.1 基本数 ...

  4. Python学习笔记之入门基础

    课程链接:Python入门教程--基础阶段_哔哩哔哩_bilibili Python学习笔记 注释 单行注释: * 多行注释: 1.多行前面加# 2."""注释信息&qu ...

  5. Python学习笔记12_函数

    Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...

  6. [python教程入门学习]python学习笔记(CMD执行文件并传入参数)

    本文章向大家介绍python学习笔记(CMD执行文件并传入参数),主要包括python学习笔记(CMD执行文件并传入参数)使用实例.应用技巧.基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋 ...

  7. python学习笔记之编写readConfig读写配置文件

    python学习笔记之编写readConfig读写配置文件_weixin_34055910的博客-CSDN博客

  8. Python学习笔记(十一)

    Python学习笔记(十一): 生成器,迭代器回顾 模块 作业-计算器 1. 生成器,迭代器回顾 1. 列表生成式:[x for x in range(10)] 2. 生成器 (generator o ...

  9. Python学习笔记一简介及安装配置

    Python学习笔记一 参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e54 ...

  10. python学习笔记目录

    人生苦短,我学python学习笔记目录: week1 python入门week2 python基础week3 python进阶week4 python模块week5 python高阶week6 数据结 ...

最新文章

  1. FlexUnit单元测试(第三章FlexUnit事件断言)
  2. RoBERTa中文预训练模型:RoBERTa for Chinese
  3. Ubuntu bitnami gitlab 安装
  4. matlab的灰色关联,灰色关联度Matlab代码
  5. linux系统子接口配置文件,Linux网卡绑定、子接口-IP别名
  6. WF+WCF+WPF第三天-WF实现一个软件自动测试框架
  7. 学习前端开发,那你值得css的特点有哪些?
  8. activiti 定时任务和线程池
  9. win7上安装mysql数据库_mysql-windows系统安装mysql数据库
  10. 三星手机性能测试软件,三星Galaxy S III性能速测:一个字“快”
  11. Excel练习线性回归
  12. Win10应用商店和UWP应用无法连接网络解决方法
  13. Tumblr营销大法(三)
  14. 机房收费系统(二)之下机退卡
  15. 看到这些让员工无处遁形专利,我麻了
  16. IOS 监控屏幕旋转
  17. 2021高考数学成绩查询,吐血整理!2020高考数学评分细则参考,2021高考这样准备少丢分!...
  18. 干货 : 聚焦于用户行为分析的数据产品
  19. HTML学习笔记——主体标题
  20. 使用gitlab ci构建IOS包并发送通知消息到企业微信

热门文章

  1. 鼠标悬停显示图片html5,JavaScript 鼠标悬停图片,显示隐藏文本
  2. 服务器脱机状态,从脱机工作切换到联机工作
  3. getset原子性 redis_RedisAPI原子性操作及原理解析
  4. 蓝牙uuid是什么意思_小公主的吐槽非常好|米家蓝牙温湿度计2
  5. hp服务器pe系统安装win7系统安装系统安装,惠普Folio 1040 G1 u盘pe如何重装win7系统...
  6. python常用标准库的基本用法_[每天一个python小技巧]Python中标准库OS的常用方法总结...
  7. 如何在EDUIS中导出ETL字幕模板_给视频加字幕?这8个工具助力你的短视频创作!...
  8. Python中的numpy.ones()
  9. 斯威夫特山地车_斯威夫特枚举
  10. Android Volley教程