闭包不存储函数元数据,而是保存外部作用域中被引用的变量;元数据属于函数对象自身属性,装饰器中需用functools.wraps或update_wrapper显式复制以保留原函数的__name__、__doc__等。

闭包本身不用于存储函数的元数据(如 __name__、__doc__、__module__ 等),它存储的是外部作用域中被内嵌函数引用的变量,形成持久化的词法环境。元数据属于函数对象自身的属性,保存在函数对象的 __dict__ 或内置属性中,由 Python 解释器在函数定义时自动设置。
但闭包和元数据常在装饰器场景中一起出现——装饰器是闭包结构,而它包裹原函数后容易覆盖原函数的元数据。所以问题本质是:
如何在基于闭包的装饰器中,让被装饰函数仍保留自己的元数据?
关键不是“用闭包存元数据”,而是在闭包实现的装饰器里,主动把原函数的元数据复制/绑定到包装函数上。
为什么装饰后元数据会丢失?
装饰器通常这样写:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("before")
return func(*args, **kwargs)
return wrapper # 返回闭包函数
@my_decorator
def hello():
"""Say hello"""
print("Hi!")
此时 hello 实际指向 wrapper,而 wrapper.__name__ 是 "wrapper",wrapper.__doc__ 是 None —— 原函数的元数据没被继承。
如何保留元数据?用 functools.wraps
@wraps(func) 是专为解决此问题设计的工具,它内部调用 update_wrapper(),把 func 的指定属性复制给 wrapper。
from functools import wraps
def my_decorator(func):
@wraps(func) # ← 关键一行
def wrapper(*args, **kwargs):
print("before")
return func(*args, **kwargs)
return wrapper
@my_decorator
def hello():
"""Say hello"""
print("Hi!")
print(hello.__name__) # 输出: hello
print(hello.__doc__) # 输出: Say hello
@wraps(func) 等价于:
wrapper.__name__ = func.__name__wrapper.__doc__ = func.__doc__wrapper.__module__ = func.__module__wrapper.__annotations__ = func.__annotations__wrapper.__qualname__ = func.__qualname__-
wrapper.__dict__.update(func.__dict__)(部分)
手动控制元数据复制(进阶)
若只需复制特定属性,可用 update_wrapper 显式指定:
from functools import update_wrapper
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
# 只复制 name 和 doc,不复制 dict
update_wrapper(wrapper, func, assigned=('__name__', '__doc__'), updated=())
return wrapper
注意:闭包变量 ≠ 元数据
-
func.__closure__存的是被闭包捕获的自由变量(如外层函数的x、a),类型为tupleofcell对象; -
func.__name__、func.__doc__等是函数对象的直接属性,存在func.__dict__中,与闭包无关; - 想查某函数是否是闭包,看
func.__closure__ is not None;
想查它有没有文档,看func.__doc__—— 这两者来源不同、用途不同。
不复杂但容易忽略











