Python中的装饰器(Decorator) 详解
装饰器(Decorator) 是 Python一种强大的语法特性,允许在不修改原函数或类代码的前提下,动态地扩展其功能。和其名字一样,装饰器的核心思想就是"包装",即通过将函数或类作为参数传递给另一个函数(装饰器),返回一个增强后的新函数或类。
核心概念
-
函数是一等对象
Python 中函数可以像变量一样传递、赋值或作为返回值,这是装饰器的基础。 -
闭包(Closure)
内部函数可以访问外部函数的变量,即使外部函数已执行完毕。 -
语法糖
@
@decorator
等价于func = decorator(func)
,简化装饰器的应用。
示例
def my_decorator(func):
def wrapper():
print("函数执行前...")
func()
print("函数执行后...")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
输出:![]()
原理
装饰器的本质是函数嵌套,@my_decorator 等价于:
say_hello = my_decorator(say_hello)
如何处理带参数的函数
函数可以使用 *args
和 **kwargs
接受任意参数:
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"参数: args={args}, kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@my_decorator
def add(a, b):
return a + b
print(add(3, b=4))
输出:
如何保留原函数元信息
如果不使用functools.wraps,被装饰的函数的名称、文档字符串等会被替换,可能导致调试困难。因此,应该使用functools.wraps来保留原函数的元数据。
from functools import wraps
import time
def my_decorator(func):
@wraps(func) # 使用@wraps保留原函数信息
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行耗时: {end - start:.2f}秒")
return result
return wrapper
@my_decorator
def my_func():
"""模拟耗时操作"""
time.sleep(1)
my_func()
print(my_func.__name__)
输出:
编辑
如果不使用@wraps,则会输出 wrapper
带参数的装饰器
装饰器本身是可以接受参数的,但需要多一层嵌套:
from functools import wraps
def repeat(n):
"""重复执行函数 n 次"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(5)
def greet(name):
print(f"Hello, {name}!")
greet("Tom")
输出:![]()
类装饰器
类也可以作为装饰器,需实现 __call__
方法:
class Test:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"函数 {self.func.__name__} 被调用了第 {self.calls} 次")
return self.func(*args, **kwargs)
@Test
def say_hi():
print("Hi!")
say_hi()
say_hi()
输出:
装饰器的应用场景
-
日志记录
记录函数调用参数、返回值或异常。 -
性能计时
统计函数执行时间。 -
权限校验
在 Web 框架中检查用户权限(如 Flask/JWT)。 -
缓存结果
缓存函数结果避免重复计算(如@lru_cache
)。 -
路由注册
Web 框架中用装饰器定义路由(如 Flask 的@app.route
)。
注意事项
-
装饰器顺序
如果使用一个装饰器装饰另一个装饰器,是按从下到上的顺序执行:@decorator1 @decorator2 def func(): pass
等价于 func = decorator1(decorator2(func))
-
不要影响原函数行为
确保装饰器不要影响原函数的行为,如参数、返回值等。 -
正确使用wraps保留元信息
在使用装饰器时,要正确使用functools.wraps
保留元信息,方便调试。
码字不易,原创更不易,如您觉得本文对您有帮助,麻烦动动您富贵的小手,点赞、收藏、关注、订阅!!!