函数的嵌套:
这里主要介绍内部函数通过 nonlocal 关键字修改外部函数函数的局部变量(注意是局部变量) 直接上代码
def outer():
a = 100 # 局部变量
b = 'hello'
print('我是outer内,inner外的代码')
def inner():
a = 200
# global b 用于全局变量
nonlocal b # 使用 nonlocal关键字修改外部函数的局部变量
b = 'good'
print('inner内部变量a的值是 %d' % a)
print('inner内部变量b的值是 %s' % b)
print('我是inner内的代码')
inner()
print('outer里变量a的值是%d' % a) # 100
print('outer里变量b的值是%s' % b) # good
outer()
# inner() inner函数是在函数内部定义的,在函数外部不能够访问
闭包:
Python 支持函数式编程,所以存在闭包,闭包是由函数及其相关的引⽤环境组合⽽成的实体 , ⼀句话: 闭包 = 函数+引⽤环境,函数式编程中,当内嵌函数体内引⽤到体外的变量 时, 将会连同这些变量(引⽤环境)和内嵌函数体, ⼀块打包成⼀个整体返回。
如果在一个函数的内部定义了另一个函数,外部的我们叫它为外函数,内部的我们叫它内函数,那么闭包就是在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
def outer():
a = 'hello' # 局部变量
def inner():
print(a) # 内部函数使用了局部变量
return inner # 返回内部函数
# def test():
# b = 'good'
# print(b)
#
#
# test()
outer()()
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).
下面针再看一些其他场景
# eg1
def func():
b = ""
def inner_func():
b += '123'
return b
return inner_func
func()
执行这个代码时,会出现问题吗? 答案是:会出现问题,先不刨析原因,再看接下来这个代码
# eg2
def func_2():
b = []
def inner_func():
b.append(1)
return b
return inner_func
ret = func_2()
print(ret())
print(ret())
执行这个代码时,会出现问题吗? 答案是:不会出现问题
假如对eg1 的代码中 b 的值改为int 类型,然后内函数里面进行加一个任意 int 类型的数值仍然会报错
结论是:内部函数引用外部函数的变量类型为可变类型时,进行相关运算不会出现问题,当外部函数的变量类型为不可变类型时,那么就不能进行相关的运算操作。特别注意的是这里指运算操作,如果是赋值的话是可以的
那么如何解决这种问题不可变类型变量引用出错的问题呢?首先要了解闭包的作用域相关概念,引用外部变量时,这个外部变量是局域变量,对于内部函数,它可以访问到其外层作用域中声明的非局部(non-local)变量,比如eg1代码示例中的变量b。
一般情况下,函数中的局部变量仅在函数的执行期间可用,一旦 外层函数func() 执行过后,我们会认为变量a将不再可用。然而,闭包使得局部变量在函数外被访问成为可能,这就是闭包的作用。
现在应该了解到其实内部函数访问外部函数的变量 b 时,b这个变量时局部变量,但是内部函数访问的是外层作用域中声明的非局部(non-local)变量,那么在内部函数中使用关键字 nonlocal即可
那么针对eg1的代码示例可以这样修改:
# eg1
def func():
b = ""
def inner_func():
nonlocal b
b += '123'
return b
return inner_func
func()
这样就可以做不可变类型的相关运算。
针对闭包有一种特殊情况,先看代码示例:
# eg3
a = []
for i in range(3):
def fun(x):
print(x + i)
a.append(fun)
for num in a:
num(2)
可能有人会觉得这个输出是:2 3 4
实际运行代码输出为:4 4 4
这种情况是因为Python 的闭包是延迟绑定 ,这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的。
如果想得到2 3 4 的输出,代码需要调整成这样
a = []
for i in range(3):
def fun(x, i=i):
print(x + i)
a.append(fun)
for num in a:
num(2)
通过参数传递的形式,就可以处理延迟绑定的这种现象