深入浅出python闭包
来源:互联网 发布:ask软件 编辑:程序博客网 时间:2024/06/05 08:47
无敌小裤衩的这篇讲解到位举例恰当,拿过来重新整理排版分享给大家,也以备自己后续查找
PS:python3.6
周围有些同事初学python,往往对python的一些高级特性,比如生成器(Generator), 闭包(closure),装饰器(Decorator)感到有点不太容易理解,虽然这些特性并非python独有,但真的掌握了一定会让你感觉原来生活如此美好。
1.闭包介绍
闭包概念:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。举个栗子先:
def startAt(x): def incrementBy(y): return x+y return incrementBy
在函数startAt中定义了一个incrementBy函数,incrementBy访问了外部函数startAt的变量,并且函数返回值为incrementBy函数(注意python是可以返回一个函数的,这也是python的特性之一)
a = startAt(1)print('function',a)print('result',a(1))
上面代码中a其实就是一个函数,上面代码执行的结果:
function <function startAt.<locals>.incrementBy at 0x000002A78C201510>result 2
从结果我们不难看出,a是函数incrementBy而不是startAt这个有点绕,但是并不难理解,因为return回来的是incrementBy函数。
print('a.function.name',a.__name__)
输出是:
a.function.name incrementBy
如果调用函数a的话,得到的结果是传入参数的整数值加。
- 常见错误
闭包无法修改外部函数的局部变量。这个是什么意思呢?
def outerFunc(): x = 0 def innerFunc(): x = 1 print('inner x:',x) print('outer x before call inner:',x) innerFunc() print('outer x after call inner:',x)outerFunc()
如果innerFunc可以修改x的值的话,x的值前后会发生变化,但结果是:
outer x before call inner: 0inner x: 1outer x after call inner: 0
在innerFunc中x的值发生了改变,但是在outerFunc中x的值并未发生变化。
python循环中不包含域的概念。
flist = []for i in range(3): def func(x): return x*i flist.append(func)for f in flist: print(f(2))
按照大家正常的理解,应该输出的是0, 2, 4对吧?但实际输出的结果是:4, 4, 4. 原因是什么呢?loop在python中是没有域的概念的,flist在像列表中添加func的时候,并没有保存i的值,而是当执行f(2)的时候才去取,这时候循环已经结束,i的值是2,所以结果都是4。
ps:补充说明:
这之所以会发生是由于Python中的“后期绑定”行为——闭包中用到的变量只有在函数被调用的时候才会被赋值。所以,在上面的代码中,任何时候,当返回的函数被调用时,Python会在该函数被调用时的作用域中查找 i 对应的值(这时,循环已经结束,所以 i 被赋上了最终的值2)
其实修改方案也挺简单的:
flist = []for i in range(3): def makefunc(i): def func(x): return x*i return func flist.append(makefunc(i))for f in flist: print(f(2))
在func外面再定义一个makefunc函数,func形成闭包,结果就正确了。
PS:返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变
3. 闭包的作用
闭包可以保存当前的运行环境,以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。 这里需要说明的是,每次运动的起点都是上次运动结束的终点。
参考代码:
origin = [0,0]def create(pos=origin): def go(direction,step): new_x = pos[0] + direction[0]*step new_y = pos[1] + direction[1]*step pos[0] = new_x pos[1] = new_y return pos return goplayer = create()print(player([1,0],10))print(player([0,1],20))print(player([-1,0],10))
结果是
[10, 0][10, 20][0, 20]
也就是我们先沿X轴前进了10,然后沿Y轴前进了20,然后反方向沿X轴退了10,坐标分别问[10,0], [10, 20], [0, 20]。
当然,闭包在爬虫以及web应用中都有很广泛的应用,并且闭包也是装饰器的基础,这些内容笔者会在后续的文章中分别介绍,这里就不多谈了。理解了本文中的概念,你应该知道的关于闭包的知识也差不多了,请在自己的编程中尽情使用吧。
from:https://zhuanlan.zhihu.com/p/22229197
via: 无敌小裤衩
- 深入浅出python闭包
- 深入浅出JavaScript闭包
- Javascript闭包(closure) 深入浅出
- 深入浅出闭包与作用域链
- 深入浅出闭包与作用域链
- 深入浅出理解JavaScript的闭包概念
- 深入浅出理解JavaScript的闭包
- 深入浅出理解JavaScript的闭包概念
- 深入浅出理解闭包(转载)
- 深入浅出理解JavaScript的闭包概念
- Python 深入浅出
- Python 深入浅出
- Python 深入浅出
- Python 深入浅出
- Python 深入浅出
- Python中的闭包
- python中的闭包
- python学习~闭包
- leetcode 127. Word Ladder BFS广度优先遍历
- 水池数目
- 逻辑回归原理及推导过程
- 关于用g++编译后运行时出现的问题:无法定位程序输入点__gxx_personality_v0
- 页面中注册、登录、增删查改时与数据库之间的数据传递
- 深入浅出python闭包
- CSS3样式和新特性
- netty源码分析(三)Netty服务端ServerBootstrap的初始化与反射在其中的应用分析
- 医院影像检查流程的完整叙述
- 词向量源码解析:(5.7)ngram2vec源码解析之counts2shuf等
- AJAX(二)---局部刷新实现分页效果的实现
- Java 堆、栈和方法区
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
- return与finally的执行顺序与返回值问题