python 闭包函数

要先看python高阶函数

闭包

闭包的必要条件:

  1. 外部函数必须 返回 一个 函数对象
  2. 返回的函数对象内,必须引用 外部函数 的 形参 或 局部变量

返回的函数就称为: 闭包

注意:

  1. 不是全局变量
  2. 返回的函数对象内,不一定要return

闭包的作用

  1. 实现装饰器(后面会讲)
  2. 可以保存函数局部变量

改造上一节的例子

def create_add(i,j):
    def fun():
        return i+j
    '''
    注意返回的是fun,后面没有小括号
    有小括号表示调用函数
    '''
    return fun

说明:
在函数create_add()内定义了 内部函数fun() ,在fun()函数中 引用了外部函数 create_add()的 形参 或者 局部变量 ,并将 fun返回

注意:
返回的是 fun,后面 不能有小括号 ,有小括号 表示 调用函数

测试调用:

#调用create_add()时,返回的并不是求和结果,而是求和函数
add=create_add(1,1)
#打印函数的内存地址
print(add)
#后面加小括号表示调用
print(add())

另一种调用方式:

print(create_add(1,1)())
print(create_add(2,2)())

查看闭包引用的外部变量

函数对象的__closure__属性返回的是一个元组对象,包含了 闭包 引用的 全部 外部变量

print(add.__closure__)

将闭包函数改造如下:

def create_add(i,j):
    def fun():
        print("i的地址:%x"%id(i))
        print("j的地址:%x"%id(j))
        return i+j
    '''
    注意返回的是fun,后面没有小括号
    有小括号表示调用函数
    '''
    return fun
总结:

内部函数 使用了 外部函数 的 形参 或 局部变量 ,那么 内部函数 就称为 闭包 closure

可以保存函数局部变量

见下面的代码,在函数内部对全局变量进行修改

sum=0#全局变量
def test(i):
    #通过global关键字表示sum为全局变量
    global sum
    sum+=i
test(1)
print(sum)
test(2)
print(sum)
test(3)
print(sum)

上面的代码使用了全局变量,做为模块,被其他功能import时,会引起变量名冲突,可以通过闭包方式隐藏掉。

见如下代码:

def test():
    sum=0#局部变量
    def fun(i):
        #通过nonlocal关键字表示sum为外部函数定义的变量
        nonlocal sum
        sum+=i
        return sum
    return fun

#调用执行
t=test()
print(t(1))
print(t(2))
print(t(3))
总结:

test()函数的局部变量sum,可以保存住数据

闭包的注意事项

看下面的代码,并心算出结果,再编写下面的代码,查看执行结果

i=0
def create_fun():
    def fun():
        return i+i
    return fun


def create_fun2(j):
    def fun2():
        return j+j
    return fun2

f=create_fun()
f2=create_fun2(i)

i=1
print(f())
print(f2())

说明:

#只是返回fun函数的引用,但并没有运行fun函数
f=create_fun()

'''
将变量i(值为0)传给create_fun2函数,并返回fun2函数的引用,并没有运行fun2函数
注意:fun2函数内部引用外部函数的形参j,此时j的值就是0
'''
f2=create_fun2(i)

i=1
'''
执行fun函数,fun函数引用全局变量i,此时i的值是1,所以结果是2
'''
print(f())
'''
执行fun2函数,fun2函数内的变量j,值为0,所以结果是0
'''
print(f2())

经典的for循环闭包问题

看下面的代码,内部函数引入外部函数的局部变量,进行循环运算
返回的是list,里面的元素是函数

def create_fun():
    funs=[]
    for item in range(0,3):
        def fun():
            return item+item
        funs.append(fun)
    return funs

执行下面代码,心算运行结果,并做演练

funs=create_fun()
print(funs[0]())
print(funs[1]())
print(funs[2]())

结果 都是4

错误原因:
返回的fun函数引用了 外部函数 的 局部变量 ,但fun函数 不是立刻执行的 。等到 3个函数都返回时,它们所引用的 变量item已经变成了2,因此最终结果都是4

注意:
闭包引用外部函数的内部变量要小心

解决方式1-三层函数

通过 三层函数 解决,创建 第二层内部函数 (有形参),原闭包函数放入该第二层函数内(即:第三层内部函数),第三层函数内引用第二层函数的 形参,在for语句中执行第二层函数,无论该循环中变量如何更改,第二层函数 形参的值不变

def create_fun():
    '''
    再创建带形参的函数,包住真正运算的函数,并返回真正运算的函数。
    函数有三个:外部、中部、内部
    '''
    def f(i):
        def fun():
            return i+i
        return fun
    funs=[]
    for item in range(0,3):
        '''
        让中部函数立刻执行,因此循环变量item的当前值传入中部函数(),真正添加的是内部函数
        '''
        funs.append(f(item))
    return funs

解决方式2-通过列表生成式

def create_fun():
    def wrapper(j):
        def fun():
            return j + j
        return fun
    funs=[ wrapper(item)  for item in range(0,3) ]
    return funs

测试调用

funs=create_fun()
print(funs[0]())
print(funs[1]())
print(funs[2]())

练习

改造统计课表作业,作业使用类实现,现在改用纯函数实现,并且不要使用全局变量(作为模块被其他代码import时,会引起变量名冲突)


原文出处:/show_1EF2KQKYlJuX.html