0%

Python中的闭包

Python中的闭包机制简介

Introduction

闭包(Closure)是 Python 中一个非常重要且常用的概念,它是函数式编程的核心特性之一。闭包是一个函数,它“记住”了它被创建时的环境,即使在其环境之外被调用,这些变量仍然可用。换句话说:一个函数返回了另一个函数,这个返回的函数引用了其外部函数的变量,这个内部函数就是一个闭包。

闭包的三个必要条件

  1. 嵌套函数:函数内部定义了另一个函数;
  2. 内部函数引用了外部函数的变量(自由变量)
  3. 外部函数返回了内部函数

例如:

1
2
3
4
5
6
7
def outer(x):
def inner(y):
return x + y # inner 使用了 outer 的变量 x
return inner

f = outer(10) # outer 返回了 inner 函数
print(f(5)) # 输出 15,等于 10 + 5

每当函数内部引用了外部作用域的变量时,Python 会将这些变量“捕获”并保存在闭包中。
我们可以用函数的 closure 属性查看闭包中存储的内容:

1
print(f.__closure__[0].cell_contents)  # 输出 10

Usage

数据隐藏(类似于私有变量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter

c1 = make_counter()
print(c1()) # 1
print(c1()) # 2

c2 = make_counter()
print(c2()) # 1(新的闭包)

如果你在一个函数内定义了另一个函数(形成闭包),而你想在内部函数中修改外部函数的变量,就需要用 nonlocal 来声明这个变量。nonlocal 是 Python 3 中引入的一个关键字,用于在嵌套函数中声明一个变量不是局部变量,而是来自于最近一层的外部(非全局)作用域

回调函数

1
2
3
4
5
6
7
def make_multiplier(factor):
def multiply(n):
return n * factor
return multiply

double = make_multiplier(2)
print(double(10)) # 20

函数工厂

1
2
3
4
5
6
7
8
9
10
def power_factory(exp):
def power(x):
return x ** exp
return power

square = power_factory(2)
cube = power_factory(3)

print(square(5)) # 25
print(cube(2)) # 8

装饰器的本质也是闭包!

1
2
3
4
5
6
7
8
9
10
11
12
13
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper

@my_decorator
def greet(name):
print(f"Hello, {name}")

greet("Alice")

进一步的,我们可以使用装饰器来为函数计时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time

def timer(func):
def wrapper(*args, **kwargs):
start = time.time() # 开始时间
result = func(*args, **kwargs) # 调用被装饰的函数
end = time.time() # 结束时间
print(f"函数 `{func.__name__}` 运行耗时:{end - start:.6f} 秒")
return result
return wrapper

@timer
def slow_function():
time.sleep(2) # 模拟耗时操作
print("任务完成")

slow_function()

再进一步的,我们对装饰器进行增强,使其能够同时实现:

  • ✅ 打印日志(函数名称和参数)
  • ⏱ 计时执行时间
  • ⚠️ 自动捕获异常并打印错误信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time
import functools
import traceback

def log_timer_exception(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"[LOG] 正在调用函数 `{func.__name__}`")
print(f"[LOG] 参数: args={args}, kwargs={kwargs}")

start_time = time.time()
try:
result = func(*args, **kwargs)
except Exception as e:
print(f"[ERROR] 函数 `{func.__name__}` 执行出错: {e}")
traceback.print_exc()
result = None
else:
print(f"[LOG] 函数 `{func.__name__}` 返回结果: {result}")
finally:
end_time = time.time()
print(f"[TIME] 函数 `{func.__name__}` 执行耗时: {end_time - start_time:.6f} 秒\n")

return result
return wrapper

调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@log_timer_exception
def divide(a, b):
return a / b

@log_timer_exception
def sleep_task(seconds):
time.sleep(seconds)
return f"休眠 {seconds} 秒完成"

# 测试
divide(10, 2)
divide(10, 0) # 除零错误

sleep_task(1)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
[LOG] 正在调用函数 `divide`
[LOG] 参数: args=(10, 2), kwargs={}
[LOG] 函数 `divide` 返回结果: 5.0
[TIME] 函数 `divide` 执行耗时: 0.000002 秒

[LOG] 正在调用函数 `divide`
[LOG] 参数: args=(10, 0), kwargs={}
[ERROR] 函数 `divide` 执行出错: division by zero
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
[TIME] 函数 `divide` 执行耗时: 0.000004 秒

闭包+lamba表达式

lambda 常用于创建简单闭包:

1
2
3
4
5
def make_adder(x):
return lambda y: x + y

add5 = make_adder(5)
print(add5(3)) # 8