python闭包详解_python闭包主要解决什么问题

python闭包详解_python闭包主要解决什么问题闭包首先了解一下:如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

闭包

首先了解一下:如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
 

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

#闭包函数的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
    b = 10
    # inner是内函数
    def inner():
        #在内函数中 用到了外函数的临时变量
        print(a+b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':
    # 在这里我们调用外函数传入参数5
    #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
    # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
    demo = outer(5)
    # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
    # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
    demo() # 15

    demo2 = outer(7)
    demo2()#17

从上面例子是我写的一个最简单的很典型的闭包。我估计如果是初学的小伙伴,可能很多名词都不明白是什么意思,没关系,我把这些名词按照自己的理解去解释一下~

外函数返回了内函数的引用

引用是什么?在python中一切都是对象,包括整型数据1,函数,其实是对象。
 
当我们进行a=1的时候,实际上在内存当中有一个地方存了值1,然后用a这个变量名存了1所在内存位置的引用。引用就好像c语言里的指针,大家可以把引用理解成地址。a只不过是一个变量名字,a里面存的是1这个数值所在的地址,就是a里面存了数值1的引用。
 
相同的道理,当我们在python中定义一个函数def demo(): 的时候,内存当中会开辟一些空间,存下这个函数的代码、内部的局部变量等等。这个demo只不过是一个变量名字,它里面存了这个函数所在位置的引用而已。我们还可以进行x = demo y = demo, 这样的操作就相当于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这之后我们可以用x() 或者 y() 来调用我们自己创建的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用
 
有了上面的解释,我们可以继续说,返回内函数的引用是怎么回事了。对于闭包,在外函数outer中 最后return inner,我们在调用外函数 demo = outer() 的时候,outer返回了inner,inner是一个函数的引用,这个引用被存入了demo中。所以接下来我们再进行demo() 的时候,相当于运行了inner函数
 
同时我们发现,一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用
 

外函数把临时变量绑定给内函数

按照我们正常的认知,一个函数结束的时候,会把自己的临时变量都释放还给内存,之后变量都不存在了。一般情况下,确实是这样的。但是闭包是一个特殊情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量
 
在我编写的实例中,我两次调用外部函数outer,分别传入的值是5和7。内部函数只定义了一次,我们发现调用的时候,内部函数是能识别外函数的临时变量是不一样的。python中一切都是对象,虽然函数我们只定义了一次,但是外函数在运行的时候,实际上是按照里面代码执行的,外函数里创建了一个函数,我们每次调用外函数,它都创建一个内函数,虽然代码一样,但是却创建了不同的对象,并且把每次传入的临时变量数值绑定给内函数,再把内函数引用返回。虽然内函数代码是一样的,但其实,我们每次调用外函数,都返回不同的实例对象的引用,他们的功能是一样的,但是它们实际上不是同一个函数对象
 

闭包中内函数修改外函数局部变量

在闭包内函数中,我们可以随意使用外函数绑定来的临时变量,但是如果我们想修改外函数临时变量数值的时候发现出问题了!
 
在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:

  • 1 global 声明全局变量
  • 2 全局变量是可变类型数据的时候可以修改

在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:

  • 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
  • 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。
#修改闭包变量的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer(a):
    b = 10  # a和b都是闭包变量
    c = [a] #这里对应修改闭包变量的方法2
    # inner是内函数
    def inner():
        #内函数中想修改闭包变量
        # 方法1 nonlocal关键字声明
        nonlocal b
        b += 1
        # 方法二,把闭包变量修改成可变数据类型 比如列表
        c[0] += 1
        print(c[0])
        print(b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':

    demo = outer(5)
    demo() 
# 结果
6
11

从上面代码中我们能看出来,在内函数中,分别对闭包变量进行了修改,打印出来的结果也确实是修改之后的结果。以上两种方法就是内函数修改闭包变量的方法。
 
还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量

def outer(x):
    def inner(y):
        nonlocal x
        x += y
        return x
    return inner


a = outer(10)
print(a(1))
print(a(3))

# 结果
11
14

两次分别打印出11和14,由此可见,每次调用inner的时候,使用的闭包变量x实际上是同一个。
参考资料:https://www.cnblogs.com/s-1314-521/p/9763376.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/164953.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 关于void (visit)(const ElemType &)的理解[通俗易懂]

    关于void (visit)(const ElemType &)的理解[通俗易懂]*关于void(visit)(constElemType&)的理解visit是一个函数指针,指向一个具体的函数,我们在具体使用visit时通过调用它(visit)的函数来调用它(visit)指向的函数,这个函数的形参列表为(constElemType&),看代码:template<classElemType>SeqList<ElemType&g…

  • 语音_波束形成

    语音_波束形成博客目的:构架语音波束形成的知识体系和资料汇总1.基本概念1.1全向(omnidirectional)与指向(sensitivity)的概念理想的全向麦克理想的指向麦克tips:全指向咪头and单指向咪头全指向咪头的特点:收…

  • PYTHON-列表推导式「建议收藏」

    PYTHON-列表推导式「建议收藏」1.列表推导式基本格式:[表达式for变量in旧列表]或者[表达式for变量in旧列表if条件]第一个表达式表示最终需要得到的形式#过滤长度小于等于3的人名names=[‘tom’,’lily’,’jack’,’bob’,’haha’]result=[namefornameinnamesiflen(name)>3]print(result)result=[name.capitalize()fo

    2022年10月23日
  • cacheable更新_详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用

    cacheable更新_详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用注释介绍@Cacheable@Cacheable的作用主要针对方法配置,能够根据方法的请求参数对其结果进行缓存@Cacheable作用和配置方法参数解释examplevalue缓存的名称,在spring配置文件中定义,必须指定至少一个例如:@Cacheable(value=”mycache”)@Cacheable(value={”cache1”,”cache2”}key缓存的key,可…

  • flyweight设计模式_适配器模式菜鸟

    flyweight设计模式_适配器模式菜鸟亨元模式动机模式定义实例结构要点总结笔记。动机在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价—主要指内存需求方面的代价如何在避免大量细粒度对象问题的同时,让外部客户仍然能够透明地使用面向对象地方式来进行操作模式定义运用共享技术有效地支持大量细粒度地对象。实例每一个字符都是一个字体 字体对象Fontclass Font{private: //unique object key string key; //object

  • 零散学习笔记(一)—-单相逆变电路设计

    零散学习笔记(一)—-单相逆变电路设计这几天帮别人设计然后画一个电路图,只设计电路图,没有具体实现功能。这题是一道电赛题,大家都知道设计一个电路简单,但是要具体实现功能可不是那么简单的。而本文章是最简单的一部分—电路部分,不涉及程序部分和调试。AC-DC电路设计电源输入电压为220V交流电压,在一般设计中只要是输入为220V交流电肯定需要将交流电转换成直流电压。一般有两个方法:模电方法:使用转换电路——整流(几个二极管组合起来把正负电压变成单向)–滤波(使波形平滑)–稳压(固定输出)–直流电压数电方法:使用DC-AC芯片进行转换(博

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号