闭包是Python函数式编程里的核心特性之一,指的是在一个内部函数中,对外部作用域的变量进行引用,并且外部函数的返回值是这个内部函数,那么这个内部函数就被称为闭包。闭包可以让函数携带额外的状态,不需要借助类就能实现简单的状态封装。

闭包的基本结构
要形成闭包需要满足三个条件:存在嵌套函数、内部函数引用了外部函数的变量、外部函数返回了内部函数。下面是一个最基础的闭包示例:
# 外部函数,接收一个参数x
def outer(x):
# 内部函数,引用了外部函数的变量x
def inner(y):
return x + y
# 返回内部函数
return inner
# 创建闭包实例,x被固定为10
add_ten = outer(10)
# 调用闭包,传入y的值
result = add_ten(5)
print(result) # 输出15
上面的代码中,inner函数引用了外部函数outer的变量x,当outer执行完毕后,x不会被销毁,而是会和inner函数绑定在一起,这就是闭包的核心特性。
闭包的常见使用场景
1. 实现数据封装
闭包可以模拟类的私有变量,把状态隐藏在闭包内部,避免外部直接修改。比如实现一个计数器:
def create_counter():
count = 0 # 外部函数的局部变量,相当于私有状态
def counter():
nonlocal count # 声明修改外部函数的变量
count += 1
return count
return counter
# 创建计数器实例
counter1 = create_counter()
print(counter1()) # 输出1
print(counter1()) # 输出2
print(counter1()) # 输出3
# 不同的闭包实例有独立的状态
counter2 = create_counter()
print(counter2()) # 输出1
这里count变量被封装在闭包内部,外部无法直接访问,只能通过返回的counter函数来修改,实现了简单的状态封装。
2. 延迟计算
闭包可以把参数的计算延迟到真正调用的时候,比如实现一个乘法表的延迟生成:
def lazy_multiply(a, b):
def multiply():
return a * b
return multiply
# 创建延迟计算的闭包,此时还没有执行乘法
calc = lazy_multiply(3, 4)
# 需要结果的时候再调用
print(calc()) # 输出12
3. 装饰器的基础实现
Python的装饰器本质上就是闭包的应用,下面是一个简单的日志装饰器示例:
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"函数{func.__name__}开始执行")
result = func(*args, **kwargs)
print(f"函数{func.__name__}执行结束")
return result
return wrapper
# 使用装饰器
@log_decorator
def add(a, b):
return a + b
print(add(2, 3))
# 输出:
# 函数add开始执行
# 函数add执行结束
# 5
闭包的注意事项
使用闭包的时候需要注意变量的作用域问题,尤其是在循环中创建闭包的场景,很容易出现变量绑定不符合预期的情况:
# 错误示例:循环中创建闭包
funcs = []
for i in range(3):
def func():
return i
funcs.append(func)
# 调用所有函数,预期是0,1,2,实际输出都是2
for f in funcs:
print(f())
# 正确示例:通过默认参数绑定当前循环的i值
funcs = []
for i in range(3):
def func(i=i):
return i
funcs.append(func)
# 此时输出0,1,2
for f in funcs:
print(f())
出现这个问题的原因是闭包引用的是变量的引用,而不是变量的当前值,循环结束后i的值变成了2,所有闭包都引用了这个最终值。通过默认参数的方式可以让每个闭包绑定循环时i的当前值。
闭包和函数式编程的关系
函数式编程强调函数是一等公民,闭包让函数可以携带状态,符合函数式编程的特性。在Python中,闭包经常和map、filter、lambda等函数式工具结合使用,比如用闭包生成特定的过滤函数:
def create_greater_than_filter(threshold):
def filter_func(num):
return num > threshold
return filter_func
# 创建过滤大于5的函数的闭包
greater_than_five = create_greater_than_filter(5)
nums = [1, 3, 5, 7, 9]
# 使用filter过滤
filtered = list(filter(greater_than_five, nums))
print(filtered) # 输出[7, 9]
这种写法让过滤逻辑可以灵活定制,不需要重复编写判断代码,体现了函数式编程的灵活性。