五、闭包

参考博客:https://www.cnblogs.com/JohnABC/p/4076855.html

一、概述

概念:如果一个内部函数对外部作用域的自由变量进行了引用或使用,那么这个内部函数就称为闭包(closure)。

 

我个人认为,闭包存在的意义就是它夹带了外部变量(私货),如果它不夹带私货,它和普通的函数就没有任何区别。同一个的函数夹带了不同的私货,就实现了不同的功能。其实你也可以这么理解,闭包和面向接口编程的概念很像,可以把闭包理解成轻量级的接口封装。【引自:https://www.cnblogs.com/JohnABC/p/4076855.html

支持将函数当成对象使用的编程语言,一般都支持闭包。比如Python, JavaScript。

下面利用闭包表示一元二次曲线。

#-*-coding:utf8-*-
def curve(a,b,c):
    def cur(x):
        return a*x*x+b*x+c
    return cur
#求具体a,b,c所对应的曲线
curve1=curve(2,1,1)
#利用列表解析求曲线的多个坐标值
print [(x,curve1(x)) for x in range(5)]
#根据输入x的值求对应的坐标
x=input('please enter x:')
print (x,curve1(x))
#闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的外部变量
curve1.__name__ #'cur'
curve1.__closure__
'''结果为
(<cell at 0x0638C330: int object at 0x02AC7A5C>,
 <cell at 0x0638C7D0: int object at 0x02AC7A68>,
 <cell at 0x0638C7B0: int object at 0x02AC7A68>)
'''
curve1.__closure__[0].cell_contents #2
curve1.__closure__[1].cell_contents #1
curve1.__closure__[2].cell_contents #1

上面代码中函数cur与外部变量a,b,c形成了闭包。利用闭包可以轻松地表达任意系数的一元二次曲线,进而求出曲线上的任意坐标点。如果不利用闭包,每次创建一元二次曲线函数时都需要同时说明a,b,c,x,也就需要更多的参数传递。降低了代码的可移植性。

二、注意的事项

1、闭包中是不能修改外部作用域的局部变量的

def foo():
    m=0
    def foo1():
        m=2
        print m
        
    print m  #0
    foo1()   #2
    print m  #0

2、Python3后使用nonlocal关键字可以修改外层函数中变量的值,如使用语句nonlocal a就可以显式的指定a不是闭包的局部变量。

def foo():
    m=0
    def foo1():
        #python3中使用nonlocal
        nonlocal m
        m = 2
        print(m)

    print(m)  #0
    foo1()    #2
    print(m)  #2

3、还有一个容易产生错误的事例

flist = []  
for i in range(3):  
    def foo(x): print x + i  
    flist.append(foo)  
for f in flist:  
    f(2)

上面代码期望的结果应该是2,3,4,但是结果为:4,4,4。原因在于函数只有在执行时,才会寻找函数里面变量的值。当把函数追加到flist列表时,函数还没有给i赋值,只有当函数执行时,才会去找i的值是多少。但这时在for循环后,i的值已经变为2了,以上。解决方法如下:

flist = []  
for i in range(3):  
    def foo(x,y=i): print x + y  
    flist.append(foo)  
for f in flist:  
    f(2)

三、应用(抄的)

1、当闭包执行完后,仍然能够保持住当前的运行环境。----现在还不是很明白,再看。

比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。我以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。

origin=[0,0]
legal_x=[0,50]
legal_y=[0,50]
def position(pso=origin):
    def player(direction,setp):
        legal_x=direction[0]*setp+pos[0]
        legal_y=direction[1]*setp+pos[1]
#注意!此处不能写成 pos = [new_x, new_y]         
        pos[0]=legal_x
        pos[1]=legal_y
        return pos
    return player


player = create()  # 创建棋子player,起点为原点  
print player([1,0],10) 
print player([0,1],20)  
print player([-1,0],10)

2、闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。

def make_filter(keep):  
    def the_filter(file_name):  
        file = open(file_name)  
        lines = file.readlines()  
        file.close()  
        filter_doc = [i for i in lines if keep in i]  
        return filter_doc  
    return the_filter  

如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序

filter = make_filter("pass")  
filter_result = filter("result.txt")  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值