地位:

闭包 和函数有关系

解释:

python中一切皆对象:
函数可以赋值给变量,例如 a = def func(), 
可以把函数当做参数,传入一个函数
可以把函数当做一个函数的返回结果

示例:

Python中允许的正确的调用方法:
    def curve_pre():
        def curve():
            print('This is a funcion')
        return curve        #函数作为返回值
    func = curve_pre()
    func()
    #产生调用,输出 This is a funcion

将上述示例扩展为闭包:

注意:
    闭包内的变量与闭包外的变量没有关系
示例:
    def curve_pre():
        a = 25              #a在curve外部
        def curve(x):
            return a*x*x
        return curve        #函数curve作为返回值
    func = curve_pre()
    print(func(2))          #打印100
外部变量对一般函数的影响:
    a = 10
    def f(i):
        return a*i
    print(f(2))             #打印20
    函数外面的a影响到了函数内a的值
外部变量对闭包的影响:
    a = 10 
    print(func(2))          #打印100
    调用外面的a没有影响到函数内a的值,def curve(x)内的a仍然是def curve_pre()内的a的值
    上述就是闭包的现象

闭包定义:

由函数以及函数定义时外部的变量构成的整体,叫闭包
闭包 = 函数 + 原函数所处环境的变量(原函数外部)

注意:

上述函数所处环境的变量不能是全局变量,即:至少需要两个结构体嵌套

闭包内的环境变量:
    保存在curve_pre().__closure__内
    print(func.__closure__)
    #输出:(<cell at 0x0000000001158CA8: int object at 0x00000000539604D0>,)
    print(func.__closure__[0].cell_contents)
    #输出:25

注意:

单一函数 + 不同的外部变量 = 多种不同的闭包(类似设计模式的工厂模式)

闭包的调用方式:

正常非闭包函数的调用:
代码:
    def func1():
        a = 10
        def func2():
            a = 20
            print("func2's a = ",a) # 20    运行顺序:2
        print("func1's a = ",a)     # 10    运行顺序:1
        func2()
        print("func1's a = ",a)     # 10    运行顺序:3

    func1()
注意:
    上述是一个函数的调用,不是一个闭包,可以使用__closure__来判断是否为闭包

测试是否是闭包:
    def func1():
        a = 10
        def func2():
            a = 20
            return a
        return func2
        func2()
    func1()
    f = func1()
    print(f.__closure__) #输出:None
原因:
    func2中的a被当做了局部变量,此时func2函数内并没有产生对外部变量的引用!
    所以,并没有构成一个闭包

修改为闭包的方式:
    def func1():
        a = 10
        def func2():
            #a = 20        将局部变量a注释
            c = a * 20
        return func2
        func2()
    func1()
    f = func1()
    print(f.__closure__) 
    #输出:(<cell at 0x0000000001108CA8: int object at 0x00000000539602F0>,)
成为闭包的原因:
    将func2中的局部变量a去掉后,只要func2中产生对外部变量a的使用,就可以被作为闭包
    闭包一定要引用外部环境的变量

闭包的应用:

要求:
    对于x,y    按顺序x=3,y=3;x=5,y=8;x=6,y=14
本质:
    需要对中间变量进行保存
非闭包实现:(失败)

    origin = 0
    def walk(step):
        new_pos = origin + step     #这一步origin是外面的全局变量
        origin = new_pos            
        #此处的赋值会出错,因为如果函数内部有赋值操作,那么origin会变成局部变量,从而导致上一句中找不到origin的定义
        return origin
    print(walk(3))
    print(walk(5))
    print(walk(6))

上述代码修改为:(借助global,成功)
    origin = 0
    def walk(step):
        global origin           #显式的声明全局变量之后,就不会讲origin作为局部变量
        new_pos = origin + step      
        origin = new_pos             
        return origin
    print(walk(3))  #3
    print(walk(5))  #8
    print(walk(6))  #14

闭包方式:(失败)
    origin = 0
    def func1(pos):
        #pos成为了环境的变量
        def walk(step):
           new_pos = pos + step 
           pos = new_pos    
           #此处报错UnboundLocalError: local variable 'pos' referenced before assignment
           return new_pos
        return walk
    tour = func1(origin)
    print(tour(3))  #3
    print(tour(5))  #8
    print(tour(6))  #14

修改为:(借助nonlocal,成功)
    origin = 0
    def func1(pos):
        #pos成为了环境的变量
        def walk(step):
           nonlocal pos     #显式声明pos是一个本地变量
           new_pos = pos + step 
           pos = new_pos    
           #此处报错UnboundLocalError: local variable 'pos' referenced before assignment
           return new_pos
        return walk
    tour = func1(origin)
    print(tour(3))  #3
    print(tour.__closure__[0].cell_contents)  #3
    print(tour(5))  #8
    print(tour.__closure__[0].cell_contents)  #8
    print(tour(6))  #14
    print(tour.__closure__[0].cell_contents)  #14

使用闭包的优点:(函数式编程)
    没有使用全局变量origin,所有的变量操作均在闭包内部
    闭包+nonlocal关键字可以完成中间变量的记录,打印__closure__[0].cell_contents也会发现,闭包确实记录了中间变量

闭包的扩展:

可以实现设计模式中的;工厂模式
闭包内的变量会常驻内存,使用时要注意
闭包不是函数式编程的全部,只是一种体现