首页 > 编程语言 >Python函数闭包(看一遍就懂系列)

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

时间:2023-03-26 22:07:59浏览次数:39  
标签:闭包 outer 函数 Python res 看一遍 inner print


闭包的定义

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

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

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


标签:闭包,outer,函数,Python,res,看一遍,inner,print
From: https://blog.51cto.com/u_14990501/6150872

相关文章

  • Python多任务-多线程-多进程-协程-进阶学习
    --多任务-多线程-多进程-协程-进阶学习--文中所提到的案例参考:GITHUB中项目文件夹https://github.com/FangbaiZhang/Python_advanced_learning/tree/master/02_Python_ad......
  • CMD窗口中运行python脚本(虚拟环境下运行py文件)
    项目文件夹创建虚拟环境参考博文:WIN10系统下的Python3.7安装虚拟环境virtualenv和创建Web网站下面以Python_advanced_learning项目文件中的005_MiniWeb_Web服务器_实现模......
  • python+selenium实现在某瓣上自动批量获取中文电影的英文名称
    一、需求背景知道一些电影的中文名称,但是想在国外网站上,查一下这些中文电影的评分。因此就必须知道这些中文电影的英文名称是什么,由此诞生次脚本 二、实现思路通过sele......
  • python3实现阿里云短信发送功能
    #-*-coding:utf-8-*-importuuidimportsysimportjsonimportuuidfromaliyunsdkcore.clientimportAcsClientfromaliyunsdkcore.profileimportregion_pr......
  • Python实例讲解 -- 操作数据库 附mysqldb win32 py2.7下载
    mysql是一个优秀的开源数据库,它现在的应用非常的广泛,因此很有必要简单的介绍一下用python操作mysql数据库的方法。python操作数据库需要安装一个第三方的模块,在http://mysq......
  • Python实例讲解 -- 解析xml
    doc.xml <?xmlversion="1.0"encoding="utf-8"?><info><intro>信息</intro><listid='001'><head>auto_userone</head><name>Jordy</name>......
  • Python字符串的encode与decode
    首先要搞清楚,字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成......
  • Python 列表 list 数组 array
    Python中的列表(list)类似于C#中的可变数组(ArrayList),用于顺序存储结构。 创建列表 sample_list=['a',1,('a','b')]Python列表操作 sample_list=['a','b',0,1,3]得到......
  • Python实例讲解 -- 定时播放 (闹钟+音乐)
    自己写的闹钟,只可以播放wav格式的音频。 importtimeimportsyssoundFile='sound.wav'not_executed=1defsoundStart():ifsys.platform[:5]=='linux':......
  • Python实例讲解 -- 图片处理
    虽然图像处理的最佳工具是MATLAB的图像处理工具箱,但是在进行一些“简单的”图像处理任务或者大批量的简单图像处理任务的时候,Python图像处理的方法更有优势。 1.引言: ......