第20讲:内嵌函数和闭包
一 内嵌函数
1 定义:内嵌函数指的是在一个函数体内部定义的函数,可以称它为函数的函数,也就是子函数,外部的函数称之为母函数,就类似局部变量和全局变量
2 特点:子函数体内定义的变量只在其函数内部有效,在其内部可以调用母函数定义的变量,但无法直接修改母函数体内定义的变量。
3 语法框架:
def fun1(): print('fun1()在被调用') def fun2(): print('fun2()在被调用') fun2()
4 调用结果
在fun1()外部是无法调用它的内嵌函数fun2()的
>>> fun1() fun1()正在被调用 fun2()正在被调用 >>> fun2() Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'fun2' is not defined >>>
二 闭包
1 定义:闭包是函数里面嵌套函数,外层函数返回里层函数,这种情况称之为闭包
- 闭包是一种满足特定要求的内嵌函数(子函数),其外部的函数称为母函数,则当子函数体内有对母函数体内定义的变量的引用时我们称这个子函数为一个闭包
- 但当母函数和其闭包都定义了参数时,由于在母函数体外是无法直接对闭包进行函数的调用的,为了能够实现对闭包的调用,需要在母函数内增加一条返回闭包函数名本身的语句,这个时候调用母函数后返回的是一个闭包的函数对象,就可以通过这种方法间接的调用闭包函数
2 特点:闭包是概念,不是某种函数类型,和递归的概念类似,就是种特殊的函数调用
3 功能:闭包可以得到外层函数的局部变量,是函数内部和函数外部沟通的桥梁
4 举例:
代码: >>> def funX(x): ... def funY(y): ... return x*y ... return funY #调用不执行funY,返回了一个函数funY ... 执行结果: >>> i = funX(2) #funX的函数返回值是在其内部定义的一个函数funY,并且这个函数没有被执行 >>> type(i) <class 'function'> >>> i <function funX.<locals>.funY at 0x000000304E17B790> >>> i(5) # 相当于funX(2)(5)调用funY,此时函数funY才真正被执行 10 >>>
嗯,突然想改一下程序,结果它这样报错了:
>>> def funX(x): ... def funY(y): ... return x*y ... return funY() # 调用funX()的过程中,funY()直接被执行了,但是参数y没有被赋值,会报错,所以程序执行到第一行之后就永远无法执行下去了 ... >>> i = funX(4) # funX()只能传一个参数,意味着y永远无法被赋值 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in funX TypeError: funY() missing 1 required positional argument: 'y' >>> i = funX(4)(4) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in funX TypeError: funY() missing 1 required positional argument: 'y' >>> i = funX(4,5) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: funX() takes 1 positional argument but 2 were given >>>
其实还是没太搞懂这个逻辑,return funY和return funY()到底有啥区别?
我大概明白了,return funY这个语句并没有调用funY函数,因为函数本身就是一个对象,所以该语句相当于把指向funY函数的标签当作返回值了,所以在用i = funX(4)调用funX函数的时候得到的是funX函数内部的返回值——也就是指向funY函数的标签,然后再引用这个标签给funY函数的变量y赋值,即可得到x*y的结果
而return funY()语句相当于返回调用函数funY后的结果,但在函数调用过程中没有给y赋值,所以导致最后程序报错了,下面例子中用增加参数y并没有赋值会报错也是差不多的原因
>>> def funX(x): ... def funY(y): ... return x*y ... print(funY(y)) ... return funY(y) ... >>> i = funX(2) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in funX NameError: name 'y' is not defined
三 nonlocal关键字
1 功能:在内嵌函数中修改外部函数的局部变量
2 举例说明为什么需要nonlocal关键字:
>>> def fun1(): ... x = 5 ... def fun2(): ... x *= x ... return x ... return fun2 ... >>> fun1() <function fun1.<locals>.fun2 at 0x000000304E17B670> >>> fun1()() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in fun2 UnboundLocalError: local variable 'x' referenced before assignment >>>
报错原因分析:x *= x的计算步骤:
(1)fun2函数内部检测到存在外部变量x
(2)为了保护外部变量x,函数fun2重新创建了一个内部变量,不过它的名字仍旧是x
(3)计算x*x,fun2内部变量x没有赋初值,程序报错; (其实感觉这里更像是程序内部出现了二义性,两个x相乘到底是fun2内部变量x两两相乘、fun2外部x变量两两相乘还是fun2内部x乘以fun2外部、fun1内部的x,程序根本不知道)
所以引入了nonlocal关键字,它的作用相当于把这两个变量合二为一了(不知道这么理解对不对)
正确代码如下:
>>> def fun1(): ... x = 5 ... def fun2(): ... nonlocal x ... x *= x ... return x ... return fun2 ... >>> i = fun1() >>> type(i) <class 'function'> >>> i() 25 >>>