是什么?

当一个函数内部定义了另一个函数,并且内部的函数引用了外部函数的变量,当外部函数返回内部函数时,就形成了一个闭包。

闭包的特点包括: 它可以访问其外部函数中定义的变量,即使外部函数已经执行完毕。这些变量会被“包裹”在闭包中,保持其状态。

作用?

保持变量状态

本质是利用变量作用域,实现内置,

记忆和缓存、私有方法和缓存

与 propery 实现的有啥区别

有哪些应用场景

缓存的实现,要求:唯一的入口,保持变量状态。闭包都可以实现

闭包与装饰器的关系?

闭包的变量查找规则 (LEGB):

闭包的变量作用域

# L - Local(最内层)
# E - Enclosing(闭包外层)
# G - Global(全局)
# B - Built-in(内置)
 
x = 10  # 全局变量
 
def outer():
    x = 20  # 闭包外层变量
    def inner():
        # x = 30  # 如果有局部变量会优先使用
        return x  # 没有局部变量时,去外层找
    return inner
 
f = outer()
print(f())  # 输出20,使用了闭包外层的值

闭包的取值逻辑

闭包中的变量是动态查找的:

# 案例1:展示动态查找
x = 1
def make_func():
    return lambda: x  # 没有把x固定下来
 
f = make_func()
print(f())  # 输出1
x = 2
print(f())  # 输出2,因为每次调用都重新查找x
  • 默认参数是在函数定义时就求值:
# 案例2:展示默认参数的行为
x = 1
def make_func():
    return lambda x=x: x  # 把当前的x值固定为默认参数
 
f = make_func()
print(f())  # 输出1
x = 2
print(f())  # 还是输出1,因为x的值在创建lambda时就固定了

这个行为在 Python 中很重要,特别是要注意:

  1. 循环中创建闭包的常见陷阱:
funcs = []
# 错误写法
for i in range(3):
    funcs.append(lambda: i)  # 所有函数都引用同一个i
print([f() for f in funcs])  # [2, 2, 2]
 
funcs = []
# 正确写法
for i in range(3):
    funcs.append(lambda i=i: i)  # 每个函数都有自己的i值
print([f() for f in funcs])  # [0, 1, 2]
  1. 可变对象的特殊情况:
# 对可变对象要特别小心
data = []
f = lambda: data
data.append(1)
print(f())  # 输出[1],因为list是可变对象
 
# 如果要固定可变对象的状态
data = [1, 2]
f = lambda data=data.copy(): data  # 创建副本作为默认参数
data.append(3)
print(f())  # 仍然输出[1, 2]