Python 返回函数+闭包小结
返回函数
即函数的返回值可以为一个函数
// 一个例子
def outer():#外函数
t = [0]
def inner():#内函数
t[0] += 1
return t[0]
return inner #外函数返回内函数的引用;
实现机制:
- 调用外函数;demo=outer()
- 外函数把每次传入的临时变量数值绑定给内函数
- 再返回内函数的引用——return inner
引用释义:我们定义了内函数,其内容为 t[0] += 1 return t[0],在内存中会占用一部分空间,而函数名inner实际上是一个变量,存着这个函数内容占用的内存的位置的引用,在命令行输入inner,会出现如下的关于函数所在位置的引用 <function main.createCounter..counter> - 再进一步进行demo() 的时候,相当于运行了inner函数
闭包
外函数中定义了内函数,内函数运用了外函数的临时变量(外函数绑定给内函数的局部变量/闭包变量),外函数返回内函数的引用,这样便构成了一个闭包。
闭包两个重要的知识点:
- 内函数可以修改临时变量/外函数的输入参数(闭包变量);
- 返回的函数并没有立刻执行,而是直到调用了f()才执行
修改外函数的临时变量
方法有以下几种:
- 临时变量采用可变对象,如list,上面所举的例子即为将临时变量设置为可变对象的情况;
t = [0]
- 用关键字nonlocal声明临时对象:
nonlocal XX
nonlocal 会使得所列出的名称指向之前在最近的包含作用域中绑定的除全局变量以外的变量
——nonlocal 会使得其所列出的名称XX不是局部变量空间的变量,需要向上一层变量空间找这个变量.
如下例:
def outer():#外函数
t = 0
def inner():#内函数
nonlocal t #需要声明的t在外函数中为临时变量
t += 1
return t
return inner #外函数返回内函数的引用
如果代码中没有 nonlocal t,会报错:
UnboundLocalError:local variable 't' referenced before assignment
(局部变量错误:赋值前引用局部变量t)
我们的原意是要引用外部变量(即在外函数中定义并赋值的t),但是由于内函数中t += 1语句的存在,这种情况下编译器/解释器会将t视为一个局部变量;
小结:当对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量。使用 nonlocal对其进行声明之后,向上一层变量空间找这个变量。
再举一个栗子
def outer(x):
def inner(y):
nonlocal x #需要声明的x在外函数中为输入参数
x+=y
return x
return inner
这个例子中,调用函数时如果写成:
demo=outer(x)
print(demo())
则会报错:
TypeError:inner() missing 1 required positional argument: 'y'
需要写成demo(XX),XX指的是y值
使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行,过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量。这意味着在内函数中若修改了闭包变量,每次调用完内函数,闭包变量都会发生变化。
接上例,验证一下闭包变量随着内函数多次调用的变化
demo=outer(10) #即x---10
print(demo(1)) #即y---1
print(demo(10)) #即y---10,然而此时x不是10,而是11,即在第一次调用demo(1)后,闭包变量x变为11
print(demo(10))
得到的结果为11,21,31
返回的内函数并不立刻执行
举个例子:廖雪峰python教程返回函数中的例子
def outer():
fs = []
for i in range(1, 4):
def inner():
return i*i
fs.append(f)
return fs
先提一句:关于第大一条变量的问题,在这个例子中,内函数的定义中不含有关于i值的赋值语句,所以不会报错UnboundLocalError
看外函数的返回函数fs,它是一个list,按照函数的循环控制,我们知道这个list中包含三个元素,每个元素分别为一个函数,通过以下语句
f1, f2, f3 = outer()
我们将这三个元素赋值给f1, f2, f3。在命令行中输入f1, f2, f3,得到如下,可以看出三者均为函数
(<function __main__.count.<locals>.f>,
<function __main__.count.<locals>.f>,
<function __main__.count.<locals>.f>)
调用f1()、f2()、f3()得到相应的结果
print(f1())
print(f2())
print(f3())
为什么得到的是9,9,9而不是1,4,9
这时我们注意到三点:
- 对于内函数inner来说,i是外部变量而不是局部变量;
- 返回的内函数并没有立刻执行,调用f1()、f2()、f3()时才执行;
- 如果外函数在结束的时候发现有自己的闭包变量将来会在内部函数中用到,就把这个闭包变量绑定给了内部函数,然后结束外函数。
直观上,我们会理解依据循环控制为当i=1时,return 1 * 1;当i=2时,return 2 * 2……
但实际上返回的内函数并没有立刻执行,因为只有外函数结束之时才会将闭包变量值绑定给内函数,再次调用内函数时才会将闭包变量带入内函数执行。
所以在此例中,返回的内函数时并没有把i值带入表达式 i * i 中,即当i=1时,return i * i ,当i=2时,return i * i……对于内函数inner来说,外部变量i随着循环不断变化,直到外函数结束时(也就是当i=3时,return i * i 后),外函数将i这个闭包变量绑定给内函数,此时绑定给内函数的i值为3。当调用f1()、f2()、f3()时将闭包变量i=3带入内函数执行。
参考的blog:
https://www.cnblogs.com/Lin-Yi/p/7305364.html
http://www.cxlzl.cn/article/12
https://www.liaoxuefeng.com/wiki/1016959663602400/1017434209254976