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:

闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。

 

 







posted @ 2021-04-03 18:00  cheng4632  阅读(593)  评论(0编辑  收藏  举报