在介绍Python中闭包的知识点之前,我们首先来了解一下什么是全局变量和局部变量
1.全局变量:在函数体外部声明的变量,所有函数都可以访问;
2.局部变量:在函数体内部声明的变量,仅限于函数内部使用;
在Python中全局变量和局部变量有以下几点关系:
- 当局部变量和全局变量名称相同时,采用就近原则;
- 如果全局变量是不可变的(比如字符串),在函数中进行修改需要添加global关键字;
- 如果全局变量是可变的(比如列表),在函数中修改的时候就不需要添加global,可直接修改;
- locals()查看本地变量有哪些,以字典的形式输出;
- globals()查看全局变量有哪些,以字典的形式输出(注意里面会有一些系统的键值对)。
name = 'xiaoming' # 不可变
list = [1, 2, 3, 4, 5] # 可变
def func():
global name # 修改全局变量,则需在函数内部声明:global 变量名
names = 's' # 局部变量
print(name)
name += 'ming'
print('修改后的name是:', name)
# 修改列表
list.append(6)
print(list)
>>>修改后的name是: xiaomingmings
>>>[1, 2, 3, 4, 5, 6]
在了解全局变量和局部变量后我们再来了解一下什么是函数嵌套
函数嵌套就是在函数里面定义函数,我们称定义在函数内部的函数为内部函数,其外部的函数为外部函数。
其具有以下特点:
1.可以访问外部函数的变量;
2.内部函数可以修改外部函数的可变类型的变量;
3.内部函数修改全局的不可变变量时,需要在内部函数声明global关键字
内部函数修改外部函数的不可变的变量时,需要在内部函数中声明 nonlocal关键字
下面我们通过一个简单的示例来深入的了解一下内部函数和全局、局部变量之间的关系:
N = 10 # 全局变量
def func_1():
n = 5 # 局部变量
list_1 = [3, 6, 9, 4] # 局部变量
# 声明内部函数
def inner_func():
nonlocal n # 修改外部函数变量
global N # 修改全局变量
n = 100
# 对list_1里面的元素进行+100操作
for index, i in enumerate(list_1):
list_1[index] = i + n
list_1.sort() # 排序
N +=101 # 修改全局变量
print(N)
inner_func() # 调用内部函数
print(list_1)
func_1()
>>> 111
>>>> [103, 104, 106, 109]
在介绍完全局、局部变量和嵌套函数后,我们开始进入今天的正题------> 闭包
形成闭包条件
1.外部函数中定义了内部函数
2.外部函数是有返回值
3.返回的值是:内部函数名
4.内部函数引用了外部函数的变量
格式:
def 外部函数():
...
def 内部函数():
...
return 内部函数
下面来看一个示例:
def func_2():
a = 1
def inner_func():
b = 2
print(a,b)
return inner_func
f = func_2() # f就是内部函数,f()就表示调用函数
f()
>>>1 2
def func_3(a,b):
c = 3
def inner_func():
s = a + b + c
print('相加之后的结果是:', s)
return inner_func
# 调用func_3
# ifunc 就是inner_func() ifunc = inner_func
ifunc = func_4(1,2) # a=1,b=2 --> inner_func -->返回内部函数
# 此时返回的内部函数已经记录此时的a和b的值,所以不会受到a,b改变的影响
# 再去调用a=2,b=8 保存在本次的inner_func函数中,返回的时候返回的就是保存2,8的内部函数
ifunc_1 = func_4(2,8) # a=2,b=8 --> inner_func -->返回内部函数
# 调用返出来的内部函数
ifunc()
ifunc_1()
>>>相加之后的结果是: 6
>>>相加之后的结果是: 13
闭包的缺点:
1.作用域没有那么直观
2.因为变量不会回收,所以有一定的内存占用问题
闭包的作用:
1.可以使用同级的作用域
2.读取其他元素的内部变量
3.延长作用域
闭包总结:
1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数局部变量没有及时释放,消耗内存
3.闭包使代码变得简洁,便于代码阅读
4.闭包是理解装饰器的基础