Python装饰器原理解析与元数据保护
本文深入解析Python装饰器的底层原理,说明其本质为高阶函数的嵌套调用。重点探讨使用装饰器后函数元数据(如名称、文档)丢失的问题,并演示如何利用functools模块中的@wraps装饰器保留原始函数信息,确保代码的可维护性与规范性。
Python装饰器用法
Python的装饰器功能强大,能够处理多种场景。
然而对于初学者而言,其语法和机制可能显得不够直观。
实际上,装饰器本质上只是一个普通的函数。
import time
def log(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print("The func '{}' used {}s.".format(func.__name__, end-start))
return result
return warpper当我们以如下方式调用装饰器时:
@log
def fucok(name):
"""Fucok someone"""
print("Fucok", name)它实际上等价于执行了 fucok = log(fucok) 这一行代码。
这意味着,表面上我们调用的是 fucok("myself"),但实际执行的是 log(fucok)("myself")。这是因为在Python中,函数也是对象。
同理,如果我们定义一个带参数的装饰器 logging,其实际执行逻辑为:
func = logging(arguments)(func)即在不带参数装饰器的基础上,再增加一层函数定义即可。
import time
def logging(arguments):
def log(func):
def warpper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print("The func '{}' used {}s.".format(func.__name__, end-start))
return result
return warpper
# do something
return log但是,应用装饰器后,原函数的元数据会被覆盖,例如函数名称、文档字符串等。
以上述代码为例:
print(fucok.__name__)
print(fucok.__doc__)你会发现,函数的元信息全部丢失!fucok 的名称变成了 wrapper,其文档字符串也变为 None。
解决方法很简单:在定义装饰器时,使用 wraps 装饰器包裹接收原函数的那一层。
来自 functools 模块的 wraps 装饰器可以将原函数的元数据复制到被包装的函数上。
修改后的装饰器如下(仅增加了一行代码):
import time
from functools import wraps
def log(func):
@wraps(func)
def warpper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print("The func '{}' used {}s.".format(func.__name__, end-start))
return result
return warpper再次运行:
print(fucok.__name__)
print(fucok.__doc__)此时可以看到,函数的元信息已恢复正常。
在定义装饰器时添加 @wraps 是一个良好的编程习惯。
一个较为实用的装饰器示例见本专题的另一篇文章:函数参数类型检查。
总结
以上为个人经验总结,希望能为大家提供参考,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













