python闭包和function.__closure__特殊属性
function.__closure__ 是 Python 函数对象上的一个特殊属性,用来保存 闭包(closure)捕获的自由变量。
它的值通常是:
None- 或一个由
cell对象组成的元组
它和 function.__code__.co_freevars 配合使用。
一、什么是闭包(closure)
先看例子:
def outer():
x = 10
def inner():
return x
return inner
这里:
f = outer()
此时:
f()
# 10
问题来了:
outer()已经执行结束- 按理说局部变量
x应该销毁 - 为什么
inner()还能访问x?
因为:
Python 把 x “封”进了闭包里。
这个闭包就保存在:
f.__closure__
二、closure 长什么样
继续:
f = outer()
print(f.__closure__)
结果类似:
(<cell at 0x000001F2...: int object at ...>,)
这是:
- 一个 tuple
- 里面是 cell 对象
- 每个 cell 保存一个被捕获的变量
查看内容:
print(f.__closure__[0].cell_contents)
输出:
10
三、和 co_freevars 的关系
看:
print(f.__code__.co_freevars)
输出:
('x',)
表示:
函数依赖一个自由变量:
x
而:
f.__closure__
保存的是:
x 对应的实际值
对应关系:
| 属性 | 含义 |
|---|---|
co_freevars |
自由变量名字 |
__closure__ |
自由变量实际 cell |
四、多个闭包变量
def outer():
x = 10
y = 20
def inner():
return x + y
return inner
f = outer()
查看:
print(f.__code__.co_freevars)
输出:
('x', 'y')
查看:
for cell in f.__closure__:
print(cell.cell_contents)
输出:
10
20
五、cell 对象是什么
闭包变量不是直接保存值。
而是:
变量名 -> cell -> 真正对象
原因是:
闭包里的变量可能被修改。
例如:
def counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
这里:
c = counter()
c() # 1
c() # 2
c() # 3
因为:
多个调用共享同一个 cell。
六、闭包内部结构图
这个例子:
def outer():
x = 10
def inner():
return x
return inner
可以理解为:
inner function
│
├── __code__
│ └── co_freevars = ('x',)
│
└── __closure__
└── cell
└── 10
七、没有闭包时 closure 是 None
def foo():
return 123
print(foo.__closure__)
输出:
None
因为:
没有捕获外部变量。
八、为什么闭包重要
很多 Python 特性依赖闭包:
- 装饰器
- 回调
- 工厂函数
- 柯里化
- 延迟绑定
- FastAPI dependency
- functools.partial 的一些思想
例如装饰器:
def log(func):
def wrapper(*args, **kwargs):
print("calling")
return func(*args, **kwargs)
return wrapper
这里:
wrapper
捕获了:
func
所以:
wrapper.__closure__
里就有 func。
九、深入理解:为什么 Python 要设计 closure
核心原因:
Python 函数是“一等对象”。
函数:
- 可以返回
- 可以赋值
- 可以动态创建
- 可以携带上下文
closure 本质是:
函数 + 创建时环境
这也是函数式编程的重要基础。
十、可以直接修改吗?
理论上:
cell.cell_contents
可读。
但通常不建议直接改。
因为这是 CPython 内部机制的一部分。
十一、一个非常经典的坑
funcs = []
for i in range(3):
funcs.append(lambda: i)
for f in funcs:
print(f())
输出:
2
2
2
原因:
lambda 捕获的是:
同一个 cell
不是当时的值。
所以循环结束后:
i == 2
全部都看到 2。
解决:
funcs.append(lambda i=i: i)
这里利用默认参数提前绑定值。
十二、总结
function.__closure__:
用于保存:
函数捕获的外部变量
核心特点:
| 特性 | 说明 |
|---|---|
| 类型 | tuple 或 None |
| 内容 | cell 对象 |
| 配套属性 | co_freevars |
| 作用 | 实现闭包 |
| 本质 | 保存函数创建时的环境 |
可以把 closure 理解为:
函数对象携带的“外部作用域快照”
但它保存的不是简单值拷贝,而是:
共享 cell 引用
注意:cell 是 CPython 里的一个内置底层对象类型,专门用于实现闭包(closure)。但它:
1,不属于我们日常能直接构造的公开内置类型
2,没有 cell() 这种构造函数
3,主要由解释器自动创建
浙公网安备 33010602011771号