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,主要由解释器自动创建

posted @ 2026-05-13 13:21  悠哉大斌  阅读(16)  评论(0)    收藏  举报