Python之函数【三】(高阶函数和闭包)

文章目录

  • 前言
  • 一、高阶函数
  • 二、闭包(也称之为:闭包函数)
    • 1、浅谈闭包函数        
      • 1.1、划重点
      • 1.2、注意点
    • 2、怎么判断是不是闭包函数呢?
      • 2.1、那接下来,我们就细细的拆开解释
      • 2.2、对于这个作用域,在JavaScript中的解释
      • 2.3、闭包函数的相关元素查看
        • ​​​​​​​​​​​​​​2.3.1、闭包函数的__code__属性(编译后的函数定义体)中保存着闭包函数的局部变量和自由变量的名称
        • 2.3.2、自由变量的值查看
  • 三、nonlocal关键字的使用​​​​​​​
  • 总结


前言

记录一下自己对高级函数和闭包函数的理解,以及着重点和注意点的分析和讲解。这个章节的基础是为了理解装饰器。


一、高阶函数

什么叫高阶函数呢?

定义:如果一个函数的参数,是另外一个或几个函数,那么这个函数就是高阶函数。

怎么理解呢?通俗点说:就是一个函数中要传进来的参数就是一个函数。不是具体的字段值。

举例:

Python之函数【三】(高阶函数和闭包)_第1张图片

 注意在这里:在fun2函数里面要使用传进来的函数,那么里面必须是一个函数的完整格式,不能只是一个函数名称。

Python之函数【三】(高阶函数和闭包)_第2张图片

 Python之函数【三】(高阶函数和闭包)_第3张图片

 当传入多个函数作为参数时:

Python之函数【三】(高阶函数和闭包)_第4张图片

二、闭包(也称之为:闭包函数)

1、浅谈闭包函数

闭包这个词有点怪,其实就是一类具体一些明显特点的函数。也可以叫做:闭包函数。

对于闭包,我们先来讲闭包函数的三个特点。

(1)闭包函数内嵌套有其它函数

(2)内嵌函数引用了外层函数的变量

(3)外层函数的返回值是嵌套函数的函数名字

1.1、划重点:

第一点:闭包函数内嵌套有其它函数。也就是说:定义了一个函数fun1,那么在函数fun1中还可以定义一个函数fun2

第二点:内嵌函数引用了外层函数的变量。也就是说:在函数fun1中定义了一个变量a,现在在函数fun2中引用了fun1的变量a。

第三点:外层函数的返回值是嵌套函数的函数名字。也就是说,fun1这个外层函数的返回值,是fun2这个函数的名字。注意是名字。

Python之函数【三】(高阶函数和闭包)_第5张图片

 1.2、注意点:

(1)如果要使用这种闭包函数,那么就要先使用一个变量去接收这个函数的返回值。

(2)这个返回值就相当于是内层函数。

我们接下来细化理解一下:

依据上面的例子:a1去接收了fun1()这个函数的返回值。那么a1就等于fun2;我们使用a1就等同于使用fun2()这个函数。

如果:当函数fun1()里面传值,但是fun2()里面不传值时,调用函数的方式有些变化:

Python之函数【三】(高阶函数和闭包)_第6张图片

2、怎么判断是不是闭包函数呢?

 使用内层函数.__closure__方法来判断是否是闭包。如果返回的是一个cell对象,那么就是闭包函数。如果返回None就不是闭包函数。

注意:这里一定是内层函数去调用这个__closure__方法,内层函数也就是外层函数的返回值。

Python之函数【三】(高阶函数和闭包)_第7张图片

总结:我们可以看到闭包函数的一个特点就是,嵌套函数。另一个特点是外层函数的变量作用域在内层函数中使用。

专业术语的定义是:闭包是一种函数,一种延伸了变量作用域的函数,它会保留定义函数时变量的绑定,当定义变量的作用域不可以用了,那些绑定仍然可以被调用。

2.1、那接下来,我们就细细的拆开解释:

(1)闭包是一种函数。也就是说,闭包是个名称,全称闭包的名字也叫闭包函数。

(2)延伸了变量作用域的函数。也就是说,本来变量的作用域在函数fun1,现在把在函数fun1的变量拿到函数fun2中使用,那么这种行为就叫做延伸了变量作用域。

(3)当定义变量的作用域不可以用了,那些绑定仍然可以被调用。也就是说,当函数fun1执行完毕了(这里解释一下什么叫做执行完毕:也就是说fun1运行完,那么fun1的作用域也就跟着运行完)。那么在函数fun2中使用函数fun1的变量这种关系,是不会消除(也就是说:fun1函数运行完了,我fun2函数还是能够照样使用你fun1的作用域,对fun2函数产生不了影响)。如果消除了那就不是闭包函数了。

通过对JavaScript语言对比的新解释:那就是在一个函数里边再定义一个函数。这个内部函数一直保持有对外部函数中作用域的访问(小盒子可以一直访问大盒子但大盒子不能访问小盒子)。

对应第三点:

我们类比学习不是闭包函数的作用域:

Python之函数【三】(高阶函数和闭包)_第8张图片

 这里的作用域list_1只能在函数fun1中被应用。

2.2、对于这个作用域,在JavaScript中的解释:

 作用继承

所谓的作用域继承,就像是儿子可以继承父亲的财产一样。比如我这里有一个大的盒子作为一个父级的作用域,然后在这个大的盒子里边放一个小的盒子,作为子作用域。我们规定可以在小盒子中获取到大盒子中的东西,大盒子不能获取小盒子里的东西就称为作用域继承。

在 JS 中,道理是一样的,在一个函数里边我们再声明一个函数,内部函数可以访问外部函数作用域的变量,而外部的函数不能获取到内部函数的作用域变量。

参考博主文章:闭包:什么是闭包、闭包的作用、闭包的解决_BUG使我疯狂的博客-CSDN博客

2.3、闭包函数的相关元素查看

2.3.1、闭包函数的__code__属性(编译后的函数定义体)中保存着闭包函数的局部变量和自由变量的名称,如下:

co_varnames:查看局部变量名称

co_freevars:查看自由变量名称

顾名思义就知道这两个函数的意思了。

Python之函数【三】(高阶函数和闭包)_第9张图片

 2.3.2、自由变量的值查看

自由变量的值是保存在__closure__[freevars].cell_contents 属性中的

Python之函数【三】(高阶函数和闭包)_第10张图片

 

三、nonlocal关键字的使用

3.1、首先我们先明白一个场景。我们在一个普通函数中把局部变量变成全局变量,使用的关键字是global。

Python之函数【三】(高阶函数和闭包)_第11张图片

 

3.2、那么在闭包函数里面可以使用global关键字的形式把局部变量改变成全局变量?

答案是:不行

(1)使用global关键字修改外层函数的变量

Python之函数【三】(高阶函数和闭包)_第12张图片

 

(2)使用nonlocal关键字修改外层函数的变量

Python之函数【三】(高阶函数和闭包)_第13张图片

 参考文章:Python之闭包_python 装饰器_搬砖,攒路费的博客-CSDN博客


总结

闭包函数一定满足三个特征,但是满足这三个特征的函数不一定是闭包函数。因为:里面的关键字申明的变量,我们上面的例子就很好的解释这一点了。

说通俗一点:人会吃饭,但吃饭的不一定是人。

你可能感兴趣的:(Python_基础知识,python)