Python学习笔记: 闭包
闭包的基本定义
在计算机科学中,闭包(英语: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
分析:
- 这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,通过line的参数 k, b 说明了这两个变量的取值,这样,就确定了函数的最终形式(y = x + 1和y = 4x + 1)。只需要变换参数a,b,就可以获得不同的直线表达函数。由此,可以看出,闭包具有跟函数类似的提高代码可复用性的作用。
- 如果没有闭包,每次新创建直线函数的时候需要同时指定 k,b,x。这样,就需要更多的参数传递,也减少了代码的可移植性。
注意点:
- 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,会消耗内存。
- 但是相较于类而言,其已经大大降低了内存占用
为了进一步的对 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学习笔记: 闭包的更多相关文章
- Python学习笔记(九)
Python学习笔记(九): 装饰器(函数) 内置函数 1. 装饰器 1. 作用域 2. 高阶函数 3. 闭包 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就 ...
- Python学习笔记:装饰器
Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...
- Python学习笔记之生成器、迭代器和装饰器
这篇文章主要介绍 Python 中几个常用的高级特性,用好这几个特性可以让自己的代码更加 Pythonnic 哦 1.生成器 什么是生成器呢?简单来说,在 Python 中一边循环一边计算的机制称为 ...
- Python学习笔记(四)函数式编程
高阶函数(Higher-order function) Input: 1 abs Output: 1 <function abs> Input: 1 abs(-10) Output: 1 ...
- python学习笔记整理——字典
python学习笔记整理 数据结构--字典 无序的 {键:值} 对集合 用于查询的方法 len(d) Return the number of items in the dictionary d. 返 ...
- VS2013中Python学习笔记[Django Web的第一个网页]
前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...
- python学习笔记之module && package
个人总结: import module,module就是文件名,导入那个python文件 import package,package就是一个文件夹,导入的文件夹下有一个__init__.py的文件, ...
- python学习笔记(六)文件夹遍历,异常处理
python学习笔记(六) 文件夹遍历 1.递归遍历 import os allfile = [] def dirList(path): filelist = os.listdir(path) for ...
- python学习笔记--Django入门四 管理站点--二
接上一节 python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...
- python学习笔记--Django入门0 安装dangjo
经过这几天的折腾,经历了Django的各种报错,翻译的内容虽然不错,但是与实际的版本有差别,会出现各种奇葩的错误.现在终于找到了解决方法:查看英文原版内容:http://djangobook.com/ ...
随机推荐
- Java EE学习笔记(十)
MyBatis与Spring的整合 1.整合环境搭建 1).要实现MyBatis与Spring的整合,很明显需要这两个框架的JAR包,但是只使用这两个框架中所提供的JAR包是不够的,还需要其他的JAR ...
- NET Everywhere
NET Everywhere 8月份已经发布了.NET Core 2.0, 大会Keynote 一开始花了大量的篇幅回顾.NET Core 2.0的发布,社区的参与度已经非常高.大会的主题是.NET ...
- 二开获取yigo设计器里查询集合里中的某个SQL
package com.bokesoft.lrp_v3.mid.dongming.service; import java.math.BigDecimal; import java.util.Arra ...
- Ubuntu里let's encrypt通配符证书的自动续期更新
环境与需求: Ubuntu云服务器上,已经做好了ssl证书的免费申请,但是证书的期限是3个月,3个月到期后必须重新申请或者更新.由于k8s集群里的服务一直在使用证书,每三个月人工更新太麻烦,所以想要配 ...
- LWIP学习之一些细节
一 绑定端口后,开启监听,为何监听还要返回一个新的连接?:监听状态的连接只需要很小的内存,于是tcp_listen()就会收回原始连接的内存,而重新分配一个较小内存块供处于监听状态的连接使用. 二 t ...
- windows下指定端口号起步memcached
双击.exe启动的话,默认启动的端口是11211 ,要指定端口的话加 -p + 端口号,如: E:\tools\memcached-1.4.5-win32>memcached-1.4.5.exe ...
- Spring 顾问
1.名称匹配方法切入点顾问 接口:ISomeService public interface ISomeService { public void doSome(); public void doSe ...
- 使用 xib 设置 button 等款等高
很多时候需要使用平分的控件来布局,当然xib中可以之间使用 UIToolBar 使用 UIBarButtonItem 添加弹簧即可完成平均分布 但是,直接使用 button 也可以实现平均布局
- dubbo注解
如果还不了解Dubbo是什么或者不知道怎么搭建的可以先看一下我的上一篇文章. 首先我先来讲下提供者(也就是服务端)的配置,先上配置文件代码: <?xml version="1.0&qu ...
- Vue的computed和methods区别
1,computed里面定义的方法是以属性的方式(当然也可以以函数调用的方式)出现在html里面,而methods里面定义的方法是以函数的方式: 2,computed依赖于data里面的数据,只有相关 ...