一、认识闭包
1. 问题背景
在 Python 中,函数外部无法直接访问函数内部的局部变量,这是由 Python 的链式作用域决定的。
python
def f1():
n = 999
print(n) # 报错:name 'n' is not defined
2. 解决方案:函数嵌套
在函数内部定义嵌套函数,利用作用域特性实现外部访问。
python
def f1():
n = 999
def f2():
print(n)
return f2
result = f1()
result() # 输出:999
二、闭包的概念
1. 通俗定义
闭包 = 内层嵌套函数 + 引用外层函数变量 + 作为返回值
2. 严谨定义(维基百科)
在支持函数嵌套的语言中,如果内部函数引用了外部函数的变量,就可能产生闭包。闭包可以在函数与一组 “私有” 变量之间建立关联,让这些变量在多次调用中保持持久。
3. 闭包两大核心作用
- 作为桥梁,读取函数内部的变量
- 将外层函数变量持久保存在内存中
三、闭包的用途
(一)读取函数内部的变量
隐藏变量、保持命名空间干净,同时对外提供访问入口。
python
def tag(tag_name):
def add_tag(content):
return "<{0}>{1}</{0}>".format(tag_name, content)
return add_tag
add_tag_a = tag('a')
print(add_tag_a('Hello')) # <a>Hello</a>
(二)让局部变量持久保存在内存中
普通函数执行完毕后局部变量会被垃圾回收,闭包可让变量长期留存。
python
def create(pos=[0, 0]):
def go(direction, step):
pos[0] += direction[0] * step
pos[1] += direction[1] * step
return pos
return go
player = create()
print(player([1,0], 10)) # [10, 0]
print(player([0,1], 20)) # [10, 20]
print(player([-1,0], 10)) # [0, 20]
(三)用途总结
- 既可以长久保存变量,又不会造成全局变量污染
- 是带参数装饰器的核心原理
- 广泛用于爬虫、Web 开发、数据处理等场景
四、使用闭包的注意事项
1. 内存消耗问题
闭包会将变量常驻内存,滥用会导致内存泄漏、性能下降。解决办法:不使用时及时清理无用变量。
2. 适用场景
- 功能简单、只有一个方法:优先用闭包,代码更简洁
- 功能复杂、多方法:优先用面向对象(类)
- 学习闭包核心价值:彻底理解装饰器
3. 不可变类型变量修改问题
内层函数默认不能直接修改外层 int/str/tuple 等不可变变量,会创建新局部变量。必须使用 nonlocal 声明。
python
def outer_fun():
x = 0
def inner_fun():
nonlocal x
x = 1
print('inner x:', x)
inner_fun()
print('outer x:', x)
outer_fun() # 输出 1 和 1
4. 禁止引用循环变量 / 后续变化变量
返回闭包时不要引用循环变量,否则所有函数都会取最终值。
python
# 错误示例
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()) # 9 9 9
# 正确示例:用参数绑定当前值
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1,4):
fs.append(f(i))
return fs
f1,f2,f3 = count()
print(f1(),f2(),f3()) # 1 4 9
五、如何判断一个函数是闭包
查看函数的 closure 属性:
- 闭包会返回一个由 cell 对象组成的元组
- 通过 cell_contents 可查看捕获的变量
python
def add(x,y):
def f(z):
return x+y+z
return f
d = add(5,6)
print(d.__closure__)
for cell in d.__closure__:
print(cell.cell_contents) # 输出 5、6
六、闭包核心总结
- 闭包 = 内层函数 + 引用外层变量 + 返回内层函数
- 作用:读内部变量、持久化保存变量
- 注意:内存消耗、nonlocal、避免循环变量
- 地位:Python 装饰器的基础,进阶必备