metadata流失与带参装饰器
2个问题
- 如何解决被装饰过的函数的metadata的流失的问题?
- 带有参数的装饰器有什么用处?
如何解决被装饰过的函数的metadata的流失的问题?
# decorator def log_func(func): def wrapper(*args, **kwargs): print('Function name: {}'format(func.__name__)) return func(*args, **kwargs) return wrapper # decorated function @log_func def multiply(x, y): 'Multiply x and y' return x * y multiply(1, 3) # >> Function name: multiply # >> 3
似乎看起来没问题,但是如果我们进一步检查被装饰过后的函数multiply各种attributes,我们就会发现问题:
# original function def multiply(x, y): 'Multiply x and y' return x * y multiply.__name__ # >> 'multiply' multiply.__doc__ # >> 'Multiply x and y' # decorated function @log_func def multiply(x, y): 'Multiply x and y' return x * y multiply.__name__ # >> 'wrapper' multiply.__doc__ # >>
可以看到__doc_, __name__等函数的metadata,都在被装饰后,都丢失了。为了解决这个问题,我们可以在装饰器里,把这些metadata都复制到被装饰过后到函数里。
# original decorator def log_func(func): def wrapper(*args, **kwargs): print('Function name: {}'format(func.__name__)) return func(*args, **kwargs) return wrapper # decorator that copies metatdata def log_func(func): def wrapper(*args, **kwargs): print('Function name: {}'format(func.__name__)) return func(*args, **kwargs) wrapper.__name__ = func.__name__ # copy name wrapper.__doc__ = func.__doc__ # copy doc return wrapper
在python的functools
包里有提供一个装饰器@wraps
就专门提供这个功能,所以我们的log_func
装饰器可以重新定义为
# decorator that copies metatdata with @functools.wraps import functools def log_func(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('Function name: {}'format(func.__name__)) return func(*args, **kwargs) return wrapper # decorate function with the above decorator @log_func def multiply(x, y): 'Multiply x and y' return x * y multiply.__name__ # >> 'multiply' multiply.__doc__ # >> 'Multiply x and y'
带有参数的装饰器有什么用处?
什么是带参数的装饰器? 细心的朋友可能已经发现在上文里,我介绍的@functools.wraps
在@语法中,有一个参数func
: @functools.wraps(func)
。而常见的装饰器在@语法中是没有参数的,比如上文用到的@log_func
。类似@functools.wraps(func)
就是带参数的装饰器;装饰器可以带多个参数@decorator(x, y)
。
看一个例子。
@log_msg_func("Function name: {name}") def multiply(x, y): return x * y
在这个例子里,log_msg_func
就是一个带参数的decorator。其实就等同于
log_func = log_msg_func("Function name: {name}") @log_func def multiply(x, y): return x * y
进一步等同于
def multiply(x, y): return x * y multiply = log_msg_func("Function name: {name}")(multiply)
可见带参数的装饰器就一个返回值为一个装饰器的函数,其实就是一个“装饰器生成器“。这个例子里,log_msg_func
的参数就是一个string,返回值就是一个装饰器。 我们看看其是怎么定义的:
def log_msg_func(msg): def log_func(func): def wrapper(*args, **kwargs): print(msg.format(name=func.__name__)) return func(*args, **kwargs) return wrapper return log_func
从上面的定义里面可以看出,msg
这个string参数为要生成的装饰器log_func
提供了一个enclosing环境。有了这个log_msg_func
”装饰器生成器“ ,我们可以轻松改变log里信息,进一步避免重复的代码。这就是带参数的装饰器的用处!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2019-07-20 gitlab开机启动|启动 停止 重启
2019-07-20 Gitlab+Jenkins实现自动部署