装饰器隐藏类方法文档:如何恢复help()中的方法描述?
这个问题我太熟悉了!当你用装饰器修饰类方法后,调用help(class_name)看不到原方法的文档字符串,本质是因为装饰器默认会用一个新的函数(也就是装饰器里的wrapper)替换原方法,但这个新函数并没有继承原方法的元数据——包括文档字符串__doc__、函数名__name__这些关键信息,所以help()只能识别到装饰器内层函数的内容,而非你写的原方法文档。
functools.wraps Python标准库的functools.wraps就是专门解决这个痛点的——它会自动把原函数的所有元数据复制到装饰器返回的新函数上,让help()和其他依赖元数据的工具能正常识别原方法的信息。
先看错误示例(未处理的情况)
def my_decorator(func): def wrapper(*args, **kwargs): """这是装饰器内部的wrapper函数""" print("执行装饰器前置逻辑") return func(*args, **kwargs) return wrapper class MyClass: @my_decorator def calculate(self, x, y): """计算两个数的和 参数: x (int): 第一个数字 y (int): 第二个数字 返回: int: 两数之和 """ return x + y help(MyClass.calculate)
运行这段代码,你会发现输出的是wrapper函数的文档,完全看不到原calculate方法的说明。
用functools.wraps修复后的代码
只需要给装饰器里的wrapper加上@functools.wraps(func)即可:
import functools def my_decorator(func): # 关键一步:用wraps把原函数的元数据复制给wrapper @functools.wraps(func) def wrapper(*args, **kwargs): """这是装饰器内部的wrapper函数""" print("执行装饰器前置逻辑") return func(*args, **kwargs) return wrapper class MyClass: @my_decorator def calculate(self, x, y): """计算两个数的和 参数: x (int): 第一个数字 y (int): 第二个数字 返回: int: 两数之和 """ return x + y help(MyClass.calculate)
现在再运行help(MyClass)或者help(MyClass.calculate),就能看到原方法完整的文档字符串了!
备选方案:手动用update_wrapper复制元数据
如果因为某些场景不能用wraps语法糖,也可以手动调用functools.update_wrapper来实现同样的效果,本质上wraps就是update_wrapper的简化封装:
import functools def my_decorator(func): def wrapper(*args, **kwargs): """这是装饰器内部的wrapper函数""" print("执行装饰器前置逻辑") return func(*args, **kwargs) # 手动复制原函数元数据到wrapper functools.update_wrapper(wrapper, func) return wrapper
类形式装饰器的处理
如果你用的是类风格的装饰器,同样需要处理元数据传递,只需要在装饰器类的__init__方法里调用update_wrapper,把原函数的元数据复制到装饰器实例上:
import functools class MyDecorator: def __init__(self, func): self.func = func # 复制原函数元数据到装饰器实例 functools.update_wrapper(self, func) def __call__(self, *args, **kwargs): print("执行类装饰器逻辑") return self.func(*args, **kwargs) class MyClass: @MyDecorator def calculate(self, x, y): """计算两个数的和""" return x + y help(MyClass.calculate)
不管是函数装饰器还是类装饰器,核心都是让装饰器返回的对象(函数或类实例)继承原方法的元数据。functools.wraps是最简洁、最推荐的方式,几乎所有自定义装饰器都应该加上它,避免出现元数据丢失的问题。
内容的提问来源于stack exchange,提问作者velvel

