17函数----闭包(closure)
一、闭包
一个能记住嵌套作用域的变量值的函数
又称闭包函数或者闭合函数,其实和前面讲的嵌套函数类似,不同之处在于,闭包中外部函数返回的不是一个具体的值,而是一个函数。
一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用。
闭包将内部函数与外部函数连接起来,其实就是一种新的传参方法
闭包的意义:返回的函数对象,不仅是函数对象,同时该函数对象还带了一层作用域,则该函数无论在何处调用,优先使用自己外层包裹的作用域。
支持将函数当成对象使用的编程语言,一般都支持闭包。比如Python, JavaScript。
1、函数是一个对象,因此可以作为函数的返回结果
闭包比普通的函数多了一个 __closure__ 属性,该属性记录着自由变量的地址,也可以查看闭包的元素
def hello(great, great1): def setName(name): print(great, name, great1) return setName Hello = hello('goodmorning', 'kkkkkkk') Hello('cc') print(dir(Hello)) print(Hello.__closure__)#返回值是一个元组 print(Hello.__closure__[0].cell_contents) #goodmorning print(Hello.__closure__[1].cell_contents) #kkkkkkk print(Hello.__name__) #setName print(id(Hello))
2、Python closure必须满足以下三个条件:
1)必须有一个内嵌函数(函数里定义的函数)
2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量
3)外部函数必须返回内嵌函数
例1:下面利用闭包表示一元二次曲线。
# 用闭包表示一元二次曲线 def curve(a,b,c): def cur(x): return a*x**2 + b*x + c return cur curve1=curve(2,1,1)#求具体a,b,c所对应的曲线,即:2x^2+x+1 #利用列表解析求曲线的多个坐标值 print([(x,curve1(x)) for x in range(5)]) #根据输入x的值求对应的坐标 x=int(input('please enter x:')) print(x,curve1(x)) #闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的外部变量 print(curve1.__name__) #'cur' print(curve1.__closure__) '''结果为 (<cell at 0x0638C330: int object at 0x02AC7A5C>, <cell at 0x0638C7D0: int object at 0x02AC7A68>, <cell at 0x0638C7B0: int object at 0x02AC7A68>) ''' print(curve1.__closure__[0].cell_contents) #2 print(curve1.__closure__[1].cell_contents) #1 print(curve1.__closure__[2].cell_contents) #1
上面代码中函数cur与外部变量a,b,c形成了闭包。利用闭包可以轻松地表达任意系数的一元二次曲线,进而求出曲线上的任意坐标点。
如果不利用闭包,每次创建一元二次曲线函数时都需要同时说明a,b,c,x,也就需要更多的参数传递。降低了代码的可移植性。
例2:
from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu = index('http://www.baidu.com') #---->返回get,但是带了参数url,这样再运行百度的首页直接用baidu(), #同样,也可以定义gugoo()首页,因为闭包返回的函数带了特定的参数 # 不然的话,再次运行百度的首页就要再次传参,get(www.baidu.com) print(baidu().decode('utf-8'))
二、用途
1、当闭包执行完后,仍然能够保持住当前的运行环境。如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。
例1:
def counter(): n = 1 def incr(): nonlocal n x = n n += 1 return x return incr c = counter() #counter()---->incr,但这时候带了变量n=2 print(c()) #此时n=2 print(c()) #此时n=3 print(c()) #此时n=4
例2:一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,
接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,
当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。
origin=[0,0] # 坐标系统原点 legal_x=[0,50] # x轴方向的合法坐标 legal_y=[0,50] # y轴方向的合法坐标 def create(pos=origin): def player(direction,setp): print('pos', pos) #pos被保存了下来 new_x = direction[0]*setp+pos[0] new_y = direction[1]*setp+pos[1] #注意!此处不能写成 pos = [new_x, new_y] pos[0] = new_x pos[1] = new_y return pos return player player = create() # 创建棋子player,起点为原点,每次移动后pos的值会被保存下来 print(player([1,0],10)) #向x轴正方向移动10步 [10, 0] print(player([0,1],20)) #向y轴正方向移动20步 [10, 20] print(player([-1,0],10)) #向x轴负方向移动10步 [0, 20]
用途2:
闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。
分类:
01Python学习
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2020-04-03 02python基础----控制流程