• 12
  • 12
分享

一、非闭包

见过了在函数中调用函数本身,在函数内部定义一个函数:

def func():
    print('-----func被调用--------')
    def count_book():
        print('这个是计算买书方式的函数')
# func()是在外面定义的,可以直接调用func()
func()

在外面可以调用里面的函数吗?

1.png

不可以。相对于外部而言,def count_book()这个函数名是局部的,是函数内部的一个局部变量,所以在外部是访问不了函数内部的数据。

在函数内部可以访问外面的,但是在函数外面是访问不了里面的。

在外面定义个函数:

def login():
    print('登录')
def func():
    login()
    print('-----func被调用--------')
    def count_book():
        print('这个是计算买书方式的函数')
# func()是在外面定义的,可以直接调用func()
func()

2.png

在函数里面是可以调用的,因为def login()它是个全局变量。

要想在外面调用里面的def count_book()函数,有什么办法呢?

加个return,把这个函数给返回回来。接下来func()函数调用之后,它会有个返回值,返回值就是count_book()。用res接收下,接收到了之后,通过res()再调用。

调用方式一:

def login():
    print('登录')
def func():
    login()
    print('-----func被调用--------')
    def count_book():
        print('这个是计算买书方式的函数')
    return count_book
# func()是在外面定义的,可以直接调用func()
res = func()
res()

3.png

调用方式二:

def login():
    print('登录')
def func():
    login()
    print('-----func被调用--------')
    def count_book():
        print('这个是计算买书方式的函数')
    return count_book
# func()是在外面定义的,可以直接调用func()
# 方式二
func()()
# 方式一
# res = func()
# res()

4.png

以上代码不是闭包,只是符合闭包的前 2 个条件,不符合条件“内层函数对外部作用域有一个非全局的变量引用”。

二、闭包

1.闭包的概念

一个完整的闭包须满足以下 3 个条件:

  • 函数中嵌套了一个函数

  • 外层函数返回内层函数的变量名

  • 内层函数对外部作用域有一个非全局的变量引用

5.png

num = 100是外层函数里定义的一个变量,不是全局变量。

6.png

以上,这种形式的函数被称为闭包。

全局变量:变量是定义在模块里,哪个地方都可以用。

例如:

7.png

非全局变量:

8.png

不带参数的闭包:

def func():
    num = 100
    def count_book():
        print(num)
        print('这个是计算买书方式的函数')
    return count_book

带参数的闭包:

def func(num):
    def count_book():
        print(num)
        print('这个是计算买书方式的函数')
    return count_book
# func()是在外面定义的,可以直接调用func()
# 方式二
# func()()
# 方式一
res = func(2020)
res()

虽然num不是在外置函数中定义的,但是通过函数参数传进来的,传到func()的命名空间里面,print(num)在内部是可以引用到func()命名空间里面的值的。

这里的num不是全局变量,它是func()命名空间里面的一个变量,一个数据,是通过参数func(2020)传进来的。

这个也是闭包,也满足闭包的三个条件。

2.闭包的作用

实现数据的锁定,提高稳定性。

递归函数在函数调用的时候是这样的:

9.png

在一个函数里面调用自身的时候,它又有块区间放这个函数,它内部有块又调用了,它会继续在内存里面把这个函数给存起来,继续这样递归下去,非常占内存。

闭包,它没有递归。

函数调用的运行机制:

定义函数的时候,运行文件,Python 解释器从上往下执行代码,检测到def login()的时候,会在内存里面找一块地址,让函数名指向这个地址。

10.png

当你在下面再次调用这个函数的时候,Python 解释器直接运行这个内存地址里面的代码,也就是函数内部的代码。

11.png

代码从上往下运行的时候,检测到有个func(),来个地址把func()里面的代码,拿到地址里。下面调用的时候就相当于直接运行地址里面的代码了。

12.png

从上往下运行,又检测到一个函数count_book(),这个时候又会把这个函数名拿出来,然后再往下走,它直接返回了函数。

函数名拿出来之后,把这个函数名拿出来放到了这里,这个时候会给它再画出来一块地址。然后让这个count_book()函数指向这个地址,有在调用count_book()它的时候,会运行里面的代码。

这里没有调用,把count_book()这个函数名返回出来了。

count_book()这个函数名是在func()的命名空间里面。调用的时候返回到res这个地方来了。返回到全局变量里来了,通过res来接收下,res其实又指向这块内存地址了。在外面通过res调用的时候,就会运行这个内存地址里面的代码。

代码中有传入参数num,函数里面引用外层的变量num,这个变量和它放在同一个空间里面。

三、函数的__closure__属性

每个函数里面都有一个这样的属性:

res.__closure__

这个属性存储的是:当前的这个函数它里面的代码以及这个函数对外部非全局变量引用的一个数据。

13.png

当是闭包的时候,返回这样一个结果:

14.png

返回一个对象。这里存储的就是 2020。

将代码修改一下:

def func(num,b):
    def count_book():
        print(num)
        print(b)
        print('这个是计算买书方式的函数')
    return count_book
res = func(2020,'qinghan')
print(res.__closure__)

15.png

闭包函数引用的非全局变量,会存储在这个函数自身的一个__closure__属性里面,当要用的时候,直接从属性里面拿就行了。

通过这种方式实现数据锁定,提高数据的安全性。

闭包函数需要使用到外部变量,为了避免使用的外部的变量发生变化。内部所用到的外部的变量,给锁定到闭包函数自身的__closure__属性里面。

这时候外部的环境发生任何变化,对它都是没有影响的。同时也不会对外层的环境造成影响。

全局变量的时候返回 None:

16.png

如果一个闭包里面引用了全局变量,那么就不算闭包了,例如:

17.png

引用全局变量了就没办法实现数据锁定了。


作者:清菡

原文链接:https://www.cnblogs.com/qinghan123/p/14216066.html


  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 1.按测试阶段划分(1) 单元测试单元测试(模块测试):针对软件设计最小的单位-程序模块,进行正确性检查的测试工作单元测试需要从程序内部结构出发设计测试用例,多个模块可以平行的独立进行单元测试单元定义:C中个一个函数,Java中的一个类在图像化软件中是一个窗口( Android的首页,我的,商品详情)(2) 集成测试集成测试(组装测试):在单元测试基础上,将所有的程序模块进行有序的,递增的测试,重点测试模块之间的接口部分(3) 系统测试系统测试:整个软件系统,全面的在进行一次测试2.是否覆盖源代码白盒测试白盒测试:看代码,研究程序源代码,和程序结构黑盒测试黑盒测试:没有代码,就是看表面的效果,...
            13 13 2494
            分享
          • 一、http协议的定义HTTP是超文本传输协议的缩写。是互联网上使用最为广泛的一种网络协议,适用于www服务器传输超文本到本地浏览器传输协议。它可以使浏览器的传输更加高效,使网络传输减少。他还能使计算机能快速准确的传输超文本文档。HTTP是客户端到服务端请求与应答的标准,http协议规定了超文本传输所要遵守的规则。客户端是终端用户(浏览器),服务端是网站,当服务端发起一个指定端口的HTTP请求。二、http协议特性无状态:1、HTTP协议自身不对请求和响应之间的通信状态进行保存。(即多个请求之间服务器并不知道是否是同一个客户端的请求。)无状态存在的问题:1、由于请求之间无状态保持,服务器就无法...
            0 0 1451
            分享
          •   彭博新能源财经在最新行业评估报告中指出,英国是全球电动汽车快速充电成本最高的国家之一,快速充电的成本是加油的 1.7 倍。  在英国,那些频繁使用公共快速充电服务的司机,其年费用可能比使用汽油高出 1000 美元(IT之家备注:当前约 7085 元人民币)。当然,那些配置了家用充电桩并享受特殊公用事业费率的用户,每年可比汽油车节省 1200 美元。  英国充电运营商面临的部分挑战,源于政府对生物燃料的激励措施和电网成本的增加。与其他国家和运营商相比,英国较低的充电密度也是导致价格上涨的因素之一。尽管充电设施的安装量创新高,但英国每辆电动汽车对应的超快充电器数量仍少于其他主要国家。作者:远洋...
            0 0 140
            分享
          • 自动化测试是近几年比较火热的一个话题,想要在软件测试这个行业继续前行,就必须拥有核心竞争力,掌握自动化测试技术,是必不可少的一个技能。在《Google软件测试之道》一书中有介绍到:在Google,70%的自动化测试工作集中于单元测试,20%集中于接口测试,剩下10%才是UI测试。诚然,我们没有Google那么完善的机制和工程师文化,没必要一切照搬Google,但Google作为互联网2.0时代最耀眼的一个公司,它的技术发展方向,流程管理等可以说是不久的将来,我们也要到达的方向。选择适合自己的,落地应用,是当下我们应该做的。目前国内的互联网行业,大环境来说,还处在一个快速发展,需要流程化标准化的...
            1 3 2188
            分享
          • 在 Postman 中,用 Environments 来管理环境变量。我们在开发的过程中,往往会用到多个环境:开发环境,测试环境,UAT 环境,生产环境等。我们要调用不同环境的 API 时,只需切换 Postman 的 Environment 即可。下面,我们来具体说下 Postman 环境变量的使用方法。知识扩展:Postman 环境设置介绍第 1 步 创建环境在 Environments 选项卡中,点击 + 号按钮来创建环境。在环境中,创建变量。其中,列中 INITIAL VALUE 和 CURRENT VALUE 的区别是&nb...
            0 0 959
            分享
      • 51testing软件测试圈微信