一、基础复习
-
函数的基本用法 创建和调用函数 函数的形参与实参等等
-
函数的几种参数 位置参数、关键字参数、默认参数等
-
函数的收集参数*args **args 解包参数详解
-
函数中参数的作用域 局部作用域 全局作用域 global语句 嵌套函数 nonlocal语句等详解
二、函数的闭包(工厂函数)
1.不通过外层函数funA()访问内层函数funB()
函数进行嵌套,只能通过外层函数funA()访问内层函数funB()。
闭包也称为工厂函数。
例1和例2通过外层函数funA调用funB()
#例1:
>>> def myfunc():
x=520
print(x)
>>> myfunc()
520
>>> print(x)
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
print(x)
NameError: name 'x' is not defined
>>>
#例2:
>>> def funA():
x=880
def funB():
print(x)
funB()
>>> funB()
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
funB()
NameError: name 'funB' is not defined
>>>
>>> funA()
880
>>>
不通过funA()函数调用funB(),通过另一种方法访问内层函数funB(),也就是闭包。具体看例3和例4的解释
#例3:
>>> def funA():
x=880
def funB():
print(x)
return funB #将funB()作为返回值返回
>>> funA() #得到funB()函数的一个引用
<function funA.<locals>.funB at 0x00000208BE292488>
>>> funA()() #通过funA()()访问到内层函数funB()
880
>>> funny=funA() #调用funA()函数,结果返回给一个变量。
>>> funny() #
880
例4:
对于嵌套外层函数的作用域会通过某种形式保存下来,尽管这个函数已经调用完了,但外层作用域里面的变量是会保存下来的。
代码解释:
闭包会把你的参数保存下来,创建一个不完整的函数,也就是只有exp这个参数,后续根据刚刚说的调用了里面的参数也就是给了base参数形成完整的函数。
1.第一个return返回的是计算的结果,第二个return返回的是exp_of函数本身。
2.执行power函数,返回的是exp_of函数的引用,相当于返回了已经定义exp变量的第一个return。
3.那么square()就是一个函数,括号里写的就是base变量。
return的意义在于返回函数的结果。
>>> def power(exp):
def exp_of(base):
return base ** exp # base的exp次方
return exp_of
>>> square=power(2) #square指向了exp_of函数,exp的值是2。
>>> cube=power(3)
>>> square(2) # 2的2次方
4
>>> square(5) #5的平方
25
>>> power(2)(5) # 另一种表达,第一次square=power(2),第二次调用square(5)相当于power(2)(5)
25
>>>
例5:
代码解释:nonlocal关键字,会把内层函数x,y的结果赋值到x,y,所以每调用一次内层函数,都重新给外层赋值一次
>>> def outer():
x=0
y=0
def inner(x1,y1):
nonlocal x,y
x+=x1
y+=y1
print(f"现在,x={x},y={y}")
return inner
>>> move=outer()
>>> move(1,2)
现在,x=1,y=2
>>> move(-2,2)
现在,x=-1,y=4
课后题:
1.为什么将闭包称之为 “工厂函数”?
答:顾名思义,工厂函数就是能产生函数的函数。
解析:
>>> def power(exp):
... def exp_of(base):
... return base ** exp
... return exp_of
...
>>> square = power(2)
>>> cube = power(3)
>>> square(5)
25
>>> cube(5)
125
上面代码中,power() 函数就是一个工厂函数,根据其参数的不同,得到了两个不同的“生产线”函数,一个是 square(),一个是 cube(),前者是返回参数的平方,后者是返回参数的立方。
2.实现闭包必须要用到嵌套函数吗?
答:是的,必须的。
解析:
在本质上,闭包就是将函数内部和函数外部连接起来的桥梁。
对于嵌套函数来说,外层函数的作用域是会通过某种形式保存下来的,内层函数可以在外层函数调用周期结束后,仍旧能访问到外层函数的变量(及参数)。
3.请问下面代码会打印什么呢?
>>> def funA():
... x = 520
... def funB():
... print(x)
... return funB
...
>>> funny = funA()
>>> del funA
>>> funny()
>>> # 请问这里会打印什么内容?
答:520。
解析:
del 语句这里会删除 funA() 函数吗?
答案是“会删,但又不会完全被删”!一个对象是否会被删除,取决于它是否还有人记得(引用)它。funA() 函数这里确实是已经被删除了,但它的内部有一个 funB() 函数仍然被 funny 引用着。换言之,当 return funB 语句被执行之后,funA() 函数的“影子”就残留在内存中了。哪怕 funA() 函数被删除了,它的“影子”也会一直在,只有当引用 funB() 函数的 funny 被删除,“影子”才会被随之抹去。
4.请问下面代码执行后,x 变量的值分别是多少?
def outter():
def innerA():
x = 100
def innerB():
nonlocal x
x = 250
def innerC():
global x
x = 520
x = 880
innerA()
print(f"调用完 innerA() 函数之后,x = {x}")
innerB()
print(f"调用完 innerB() 函数之后,x = {x}")
innerC()
print(f"调用完 innerC() 函数之后,x = {x}")
outter()
print(f"此时此刻,全局变量 x = {x}")
答:
调用完 innerA() 函数之后,x = 880
调用完 innerB() 函数之后,x = 250
调用完 innerC() 函数之后,x = 250
此时此刻,全局变量 x = 520
解析:
这里需要注意的是LEGB 规则以及 nonlocal 和 global 语法。
这里要注意的关键是 x = 880 是定义在 outter() 函数中的局部变量。但相对于 innerA()、innerB() 和 innerC() 这三个函数来说,它又是处于 LEGB 中的 E(嵌套函数的外层函数作用域),使用 nonlocal x 可以修改它的值。global x 修改的是全局变量的 x(注意:如果不存在,就会创建一个)。
5.如果想要函数 funA() 的调用结果如下,它的函数体应该如何定义?
>>> # 请定义 funA() 函数,使其调用结果如下。
>>> funA(3)(4)(5)
60
答:
>>> def funA(x):
... def funB(y):
... def funC(z):
... return x * y * z
... return funC
... return funB
...
>>> funA(3)(4)(5)
60
6.挑战一下自己,请将下面这个闭包函数转换为 lambda 表达式的形式?
>>> def maker(n):
... def action(x):
... return x ** n
... return action
>>> f = maker(2)
>>> f(3)
9
>>> f(5)
25
答:
>>> f = lambda n : lambda x : x ** n
>>> g = f(2)
>>> g(3)
9
>>> g(5)
25
题目来自小甲鱼
函数(V)