2020/1/3 返回函数学习笔记
1. 闭包
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
print(f1(), f2(), f3())
注意:在定义 f1, f2, f3 的时候,只是调用了函数 count() 的外层,并没有调用函数 f() , 所以输出的结果是
9 9 9
因此, 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
此外,f1, f2, f3 的定义是列表常用的一种方式
a = [1, 2, 3]
b, c, d = a
而如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():
fs = []
for i in range(1, 4):
def f(i):
def g():
return i * i
return g
fs.append(f(i))
return fs
f1, f2, f3 = count()
print(f1(), f2(), f3())
输出就变成
1 4 9
2. 练习题
利用闭包返回一个计数器函数,每次调用它返回递增整数:
1)generator 方法
应该是最先想到的,很直观的方法
def createCounter():
def f():
i = 0
while True:
i = i + 1
yield i
g = f()
def counter():
return next(g)
return counter
注意一点,g = f() 这一步是必不可少的。
2)nonlocal 方法
def createCounter():
i = 0
def counter():
nonlocal i
i = i + 1
return i
return counter
通过 nonlocal 标识该变量是上一级函数中的局部变量 ,这里不推荐使用global 将 i 变成全局变量
i = 0
def createCounter():
def counter():
global i
i = i + 1
return i
return counter
counterA = createCounter()
counterB = createCounter()
一旦这样,counterA 和 counterB 会使用同一个 i, 导致结果出错
3)列表方法
想到 global 和 nonlocal, 自然会想到列表这类特殊情况:
def createCounter():
s=[0]
def counter():
s[0]=s[0]+1
return s[0]
return counter
这里注意,s 是一个列表,依旧是局部变量,但在嵌套函数中可以直接修改,列表的情况比较复杂,举一些例子:
a).
lis = [1]
def f():
# lis.append(0)
lis = [0]
return lis
f()
print(lis)
这种情况列表不会改变,当然加上 global 是可以的,输出为:
[1]
b).
lis = [1]
def f():
lis.append(0)
# lis = [0]
return lis
f()
print(lis)
这种情况,列表会改变,输出为:
[1, 0]
注意,上面两句话,如果同时执行,且不调换顺序的话,是会报错的,因为程序认为你是在先修改列表 lis, 再定义它
lis.append(0)
lis = [0]
local variable 'lis' referenced before assignment
c). 同样的道理,引入返回函数的情况
def f():
lis = [1]
def g():
# lis.append(0)
lis = [0]
return lis
g()
print(lis)
return g
f = f()
print(f())
通过运行可以看到,对列表整体进行操作时:
#使用 lis = [0]:
[1]
[0]
对列表具体元素进行操作时:
#使用 lis.append(0):
[1, 0]
[1, 0, 0]
d). 返回列表与返回列表元素不一样
lis = [1]
def f():
# lis.append(0)
lis[0] = lis[0] + 1
return lis
print(f(),f())
返回的是列表,所以程序记住的是列表地址,最后执行两次 f() 后输出的就是:
[3] [3]
而如果,返回的是列表的元素,程序记住的就是该元素的值:
lis = [1]
def f():
# lis.append(0)
lis[0] = lis[0] + 1
return lis[0]
print(f(),f())
输出为:
2 3
这里 append 操作道理是一样的。
最后,是练习题的测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
print('测试通过!')
else:
print('测试失败!')