Python函数闭包(看一遍就懂系列)

闭包的定义

定义:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)

下面这个就是一个简单的闭包函数

def ExFunc(n):
    sum = n
    def InsFunc():
        return sum + 1
    return InsFunc

myFunc_1 = ExFunc(10)
print(myFunc_1())

myFunc_2 = ExFunc(20)
print(myFunc_2())

输出结果

11
21

函数InsFunc是函数ExFunc的内嵌函数,并且是ExFunc函数的返回值。我们注意到一个问题:内嵌函数InsFunc中 引用到外层函数中的局部变量sum,Python会这么处理这个问题呢?先让我们来看看这段代码的运行结果。

当我们调用分别由不同的参数调用ExFunc函数得到的函数时myFunc_1(),myFunc_2()得到的结果是隔离的,也就是说每次调用ExFunc函数后都将生成并保存一个新的局部变量sum。其实这里ExFunc函数返回的就是闭包。

ExFunc函数只是返回了内嵌函数InsFunc的地址,在执行InsFunc函数时将会由于在其作用域内找不到sum变量而出错。
而在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。

现在给出引用环境的定义就 容易理解了:引用环境是指在程序执行中的某个点所有处于活跃状态的约束(一个变量的名字和其所代表的对象之间的联系)所组成的集合。闭包的使用和正常的函数调用没有区别。由于闭包把函数和运行时的引用环境打包成为一个新的整体,所以就解决了函数编程中的嵌套所引发的问题。

如上述代码段中,当每次调用ExFunc函数 时都将返回一个新的闭包实例,这些实例之间是隔离的,分别包含调用时不同的引用环境现场。 不同于函数,闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

python中的闭包从表现形式上定义(解释)为:

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,
那么内部函数就被认为是闭包(closure).比如上面的函数:sum就外部局部变量,InsFunc就是内部函数

再看一个例子:
(外层函数传入一个参数a, 内层函数依旧传入一个参数b, 内层函数使用a和b, 最后返回内层函数)

def outer(a):
    def inner(b):
        return a + b
    return inner

c = outer(8)
res = c(10)

# c是一个函数,outer(8)运行结果返回值是函数inner,
# c指向的是outer.inner,外部局部变量a就是8
# c(10)就是outer.inner(10)(但是函数运行不能这么写,该处只是比喻),即b是10
print(type(c))
print(c.__name__) #c函数运行的是inner函数
print(type(res))
print(res)
print(outer(8)(10))

输出结果:

<class 'function'>
inner
<class 'int'>
18
18

结合这段简单的代码和定义来说明闭包:
如果在一个内部函数里:inner(y)就是这个内部函数,对在外部作用域(但不是在全局作用域)的变量进行引用:
a就是被引用的变量,a在外部作用域outer函数里面,但不在全局作用域里,则这个内部函数inner就是一个闭包。

再稍微讲究一点的解释是,闭包=函数块+定义函数时的环境,
内部函数inner就是函数块,a就是环境,当然这个环境可以有很多,不止一个简单的a。

闭包使用注意事项

# 1. 闭包中是不能修改外部作用域的局部变量的
def outer():
    m = 0
    def inner():
        m = 1
        print(m)
    print(m)
    inner()
    print(m)
outer()
# 运行结果发现,最后一个m还是0
# 闭包函数不会修改外部作用域局部变量的值

输出结果

0
1
0
# 2. 闭包中常见的一个错误
def outer():
    m = 1
    def inner():
        m = m + 1
        return m
    return inner
res = outer()
print(res.__name__)
# print(res())
# 本来是想运行之后,m变成2,但是运行并没有返回2
# 上部代码右边的m已经标红,鼠标方上去,提示未找到参考即未找到变量引用
# 执行res_1()时会出现错误,执行闭包函数时,python会导入全部的闭包函数体inner()
# 来分析其的局部变量,python规则指定所有在赋值语句左面的变量都是局部变量,
# 则在闭包inner()中,变量m在赋值符号"="的左面,被python认为是inner()内部函数中的局部变量。
# 再接下来执行print(res())时,程序运行至m = m + 1时,因为先前已经把m归为inner()内部中的局部变量,
# 所以python会在inner()中去找在赋值语句右面的m的值,不会找到外部函数中的m,结果找不到值,就会报错

# 上面代码修改一下,上述问题就可以解决
def outer():
    m = 1
    def inner():
        # 指定m不是闭包的局部变量,inner中的m就可以找到外部的m
        nonlocal m
        m = m + 1
        return m
    return inner
res = outer()
print(res.__name__)
print(res())

# 我们发现上面错误代码中outer中的m是灰色,inner中的m不会找到他
# 修改后代码,内部的m就可以找到外部的m,所有m最终结果就是2

输出结果

inner
inner
2

闭包的主要用途:

# 用途1,当闭包执行完后,仍然能够保持住当前的运行环境。

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

# 具体实例参考末尾的两个例子:https://www.cnblogs.com/JohnABC/p/4076855.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值